© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
P. P. DingareCI/CD Pipeline Using Jenkins Unleashedhttps://doi.org/10.1007/978-1-4842-7508-5_11

11. Preparing a Java API Project Using Maven

Pranoday Pramod Dingare1  
(1)
Pune, Maharashtra, India
 

After a thorough explanation of configuring the jobs in Jenkins in the last chapter, this chapter starts looking at Jenkin’s real-time use in managing the end-end build lifecycle of different kinds of applications. You see how to configure different kinds of jobs and trigger them in different ways in upcoming chapters.

Different kinds of applications, such as reusable libraries called Application Programming Interfaces (APIs), web applications, RESTful API services, and so on, have their own processes for building and deploying. For example, a Java API project could be bundled in the form of a .JAR file and will be released to an artifact repository, whereas a web application developed in Java could be bundled in a .WAR file and would be deployed in server like Tomcat. To implement end-end build lifecycles of any of these applications in Jenkins, you need to first understand the different build lifecycle phases these applications need to go through, along with the tools that will help you take these applications through those different phases until they reach their final destinations.

This chapter develops a simple Java API project and the different build lifecycle phases it has to go through, along with tools like TestNg and Maven. In subsequent chapters, you’ll see how to build different kinds of Jenkins jobs to automate the release of this Java API project.

Understanding the Maven Build Tool

This section looks at how a very popular build tool called Maven can be used to build a Java API project. Maven’s primary goal is to allow a developer to comprehend the complete state of a development effort in the shortest period of time.

Before we dive deeply into Maven, you need to understand the typical workflow of the development lifecycle of a Java API and how a build tool helps developers perform them.

Typical Development Flow of a Java API Project

This section explains the typical tasks a developer needs to perform when developing a Java API project. The actual tasks may vary from project to project.

Downloading Third-Party Libraries

While developing any application, developers uses binaries, which are bundled API classes available with the development kit. In this case, it’s the JDK (Java Development Kit), as we are talking about Java API projects. Along with these native libraries, developers may need libraries developed by the API developers. These are called third-party libraries. These libraries are usually available on different web platforms called artifact or package registries. For example, https://mvnrepository.com/ is a central repository where Java API developers deploy packaged files (.JAR files) and developers wanting to use them can download them. https://www.npmjs.com/ is a package registry to release reusable node packages built using JavaScript. The Java API developer in this book needs to download these library files from the mvn repository.

Adding Downloaded Libraries to the Project Build Path

These downloaded libraries must be added to the reference libraries the project developer is working on. In the context of a Java API project, these libraries are added to the CLASSPATH of the project.

Coding and Writing Unit Test Cases

Once the required third-party and native libraries are present in the build path of the project, developers can write different API functions. Once the development of a particular unit/function is done, unit test cases are written. If developers are using a test-driven development approach (TDD), they write test cases before implementing a functionality piece.

Compiling the Application and Unit Test Cases Code

After the code is written, the developer needs to compile the API source code as well as the unit test cases code.

Running Unit Test Cases

After compiling the application’s source code and the unit test cases, developers need to run unit test cases using unit testing tools like TestNG, Pytest, NUnit, etc. These tools depend on the development environment they are using.

Bundling/Packaging the Application

After the unit testing is done and the code has been merged, the implementation is packaged into a library (.JAR) file.

Releasing it on the Artifact Repository

Once a particular library version is created, it will be released on the artifact repository so that users can download and use it.

How the Build Tool Helps Developers

You just saw a typical workflow of a Java API project. In this flow, you saw that developers have to perform a lot of tasks, including downloading the required compile-time libraries (libraries that the developer needs to compile the implementation) and testing libraries (libraries that the developer needs while testing the implementation) and runtime libraries. Then the code is compiled, there is unit testing, packaging, and deployment of the created lib package, and so on. Performing these tasks manually is cumbersome and sometimes error-prone. Hence, developers need a tool to perform these tasks. Tools that developers use to automate these tasks are called build tools. There are various build tools available, such as Apache Ant, Maven, Gradle, etc.

How to Use the Maven Build Tool to Build a Java API Project

This section explains how you can use the Maven build tool to build a Java API project. You are going to use Eclipse to build the Java API project.

You need to install the following prerequisites before you can move on with this section:
  • Java Development Kit (JDK)

  • Eclipse (the latest version)

Step 1: Creating a Workspace in Eclipse

An Eclipse workspace is a directory, a working location where all the projects created in Eclipse are stored. Start Eclipse. It will ask you for a workspace location (see Figure 11-1). You can provide the path of any folder; then click the Launch button.

An Eclipse I D E Launcher page. Below is, Select a directory as workspace, under which is, Eclipse I D E uses the workspace directory to store its preferences and development artifacts. Below it, Workspace, with D colon, backslash Jenkins Book Examples backslash typed in. A browse tab is next to it. Below is a checkbox before, Use this as the default and do not ask again. Recent Workspaces, and at the lower right, the Launch and Cancel tabs.

Figure 11-1

The window to select Eclipse Workspace location

Once you click the Launch button, the Eclipse welcome page will appear.

Step 2: Creating a Maven Project

Follow these steps to create a Maven project:
  1. 1.

    Installing the Maven Eclipse plugin: Maven has a command-line interface (CLI). You can use Maven by running its commands using a shell program. If you want to use Maven from within Eclipse, the Eclipse Maven plugin must be installed inside the Eclipse environment. In the latest versions of Eclipse, the Maven plugin is already installed. If you are using an older version of Eclipse, you may need to install the Maven plugin explicitly.

     
  2. 2.

    If Maven is already installed, you can see it in the Preferences window (see Figure 11-2), which opens after you choose the Window ➤ Preferences menu option.

     

A page on which in the left panel is a filter tab with the Maven option highlighted below it. On the right is Maven. Below, Do not automatically update dependencies from remote repositories, and Download Artifact Sources, are chosen from a list of options. At the bottom of the list is, Global Checksum Policy, with Default, typed in. Below it, to the right are the Restore defaults and Apply tabs. At the bottom right, are the tabs, Apply and Close, and Cancel.

Figure 11-2

Maven in Preferences in Eclipse

Note

To download the required plugins and project dependencies for Maven, you need Internet access. If you are working from a network that has a proxy installed, then you need to create a settings.xml file in the ${use.homer}.m2 folder and include the proxy details in that file. Refer to the “Understanding Maven’s settings.xml File” section in this chapter for more information.

  1. 3.

    Creating a Maven project: Choose the File ➤ New ➤ Other menu option.

     

This will open the Select a Wizard window.

You will see a Maven section there. Open its subsections.
  1. 4.

    Select the Maven Project option and click the Next button (see Figure 11-3).

     

A Select a Wizard page. Below is, Select a Wizard, under which is, Create a Maven Project. Below it is Wizards, with a filter entry box. Below are the options, Java, Maven, under which are Check out Maven Projects from S C M, Maven Module, and Maven Project, Oomph, and Plug-in Development. Maven Project is highlighted. At the bottom, the Next tab is highlighted.

Figure 11-3

The Maven Project option

This will open the New Maven Project window (see Figure 11-4).

A window contains New Maven Project, under which is, Select project name and location. Below it is Create a simple project, skip archetype selection, and Use the default Workspace location with a checked box beside it. Below is an entry box for Location, Add project or projects to working set, an entry box, and Advanced. At the lower right are tabs for, Back, Next, Finish, and Cancel. The Next tab is highlighted, and the Finish tab is grayed out.

Figure 11-4

The New Maven Project window

  1. 5.

    Click the Next button, which will open the Select an Archetype window.

     
Maven performs each of these tasks using plugins. Different types of applications have their own unique requirements of directory structures. For example, a webapp has a WEB-INF folder and an index.html file and an EJB application has its own requirements of directory structure. Maven provides different archetype plugins which create the appropriate directory structure. If you are developing a web application, you need the maven-archetype-webapp, which will create a required directory structure for your application. We are creating a simple Java API project, so we use the maven-archetype-quickstart plugin in this example.
  1. 6.

    Enter maven-archetype-quickstart into the Filter field and click the Next button.

     
  2. 7.

    After clicking the Next button, Maven will start downloading the artifact plugins (see Figure 11-5), which may take some time.

     

A window contains New Maven Project, under which is, Select project name and location. Below it is, Create a simple project, skip archetype selection, and Use the default Workspace location with a checked box beside it. Below is an entry box for Location, Add project or projects to working set, an entry box, and Advanced. At the lower right are tabs for, Back, Next, Finish, and Cancel. The Next tab is highlighted, and the Finish tab is grayed out.

Figure 11-5

Downloading the artifact plugins

Once the artifacts are downloaded, you will see the New Maven Project window.

Here you’ll see GroupId, ArtifactId, and Version. A Maven project is identified uniquely using these three parameters on any artifact repository, such as a MvnRepository or Nexus repository.
  • GroupId: Indicates the owner of the project or group under which similar kinds of projects are created.

  • ArtifactId: The name of a project.

  • Version: A specific release of a project available to download on the artifact repository.

For example, I used a GroupId of Pranodayd, an ArtifactId of CalculatorAPI, and a Version set to 1.0 (see Figure 11-6).

A window contains New Maven Project, under which is, Specify Archetype parameters. Below it is, Group I d, with Pranodayd typed in, Artifact I d, Calculator A P I is typed in, Version, 1.0 is typed in, and Package, Pranodayd dot Calculator A P I is typed in and highlighted. Below is an empty table titled, Properties available from an archetype, with headers Name, and Value. Next to it are the Add and Remove tabs. Below are tabs for, Back, Next, Finish, and Cancel.

Figure 11-6

The New Maven Project window with the Group ID, Artifact ID, and Version details filled in

  1. 8.

    Click the Finish button, which will start creating the Maven project.

     

Understanding the Maven Project Directory Structure

The Maven project directory is created with the name you specify in the ArtifactId field when creating the project. Let’s look at the directories and files created in the project.

src/main/java

src/main/java is the default application source code directory in which you are supposed to create your Java package directory structure. It holds the source code files. In this folder, a package directory structure with the GroupId and ArtifactId will be created.

I created a project with the GroupId set to Pranodayd and the ArtifactId set to CalculatorAPI. You can see in the src/main/java directory that a directory called Pranodayd has been created. Inside this directory there is a directory called CalculatorAPI. Inside that directory, you will see a file named App.java, which is a template demo file that I deleted.

src/test/java

This directory contains the unit test cases. It also has a package directory structure that’s created using the GroupId and ArtifactId.

Inside this directory structure, you will see Test.java, which is a template Java file for unit test cases.

Inside the project directory, you will see the pom.xml file (see Figure 11-7), which we talk about later in this chapter.

A Calculator A P I folder. On the menu bar, File, Home, Share, and View are given. The file is highlighted. Below is a taskbar that contains, Pin to quick access, Copy, Paste, and Move to, among others. On the left panel, DATA D is highlighted. On the right is a table with headers, Name, Date Modified, Type, and Size. The entry, pom, 15, 05, 2021 10, 41, X M L Document, and 3 K B are highlighted.

Figure 11-7

The file contents of the Maven project

Understanding Java API Project Code Files

API source code: Under src/main/java, inside the package directory structure, I created a file named Calculator.java. This file has basic arithmetic operations like addition, subtraction, multiplication, and division.

API unit test code: Under src/test/java, inside the package directory structure, I created Java files containing unit test cases for all arithmetic functions implemented in the API. For example TestAdditionFunctionality.java contains unit test cases for the addition function written in the API, TestSubtractionFunctionality.java contains test cases for the subtraction function, and so on.

These test cases are executed using a popular unit testing tool in the Java environment, called TestNG.

TestNG is a unit testing tool that controls the flow of unit test cases using different methods annotated with TestNG annotations, including @BeforeClass, @AfterClass, @Test, @BeforeMethod, etc. It also generates a test report.

Understanding the pom.xml File in the Java API Project

POM stands for Project Object Model. A Maven pom.xml file is the heart of any Maven project and it defines different kinds of details of a particular project. Project information is contained in the <project></project> tags.

The information shown in Listing 11-1 defines the identification of the project.
  <groupId>Pranodayd</groupId>
  <artifactId>CalculatorAPI</artifactId>
  <version>1.0</version>
Listing 11-1

Maven Project Identification Information from pom.xml

The properties section shown in Listing 11-2 defines properties such as which Java version to use to compile the project and which text encoding to use, such as ANSI, UTF8, and so on, when compiling .JAVA files.
<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
</properties>
Listing 11-2

Maven Project Properties Section from pom.xml

The <dependencies></dependencies> section (see Listing 11-3) defines the third-party libraries and at which stage those libraries are required (i.e., at the stage of compilation, unit testing, or when running the app).
<dependencies>
      <dependency>
            <groupId>org.testng</groupId>
            <artifactId>testng</artifactId>
            <version>7.4.0</version>
            <scope>test</scope>
      </dependency>
</dependencies>
Listing 11-3

Maven Project Dependencies Section from pom.xml

This API project only needs the TestNg dependencies and <scope>test</scope> defines that Maven should put TestNg JARs in the project CLASSPATH only when you run unit test cases. Once the unit test cases are done, the TestNg JARs should be removed from the project CLASSPATH. The scope defines at which stage of the lifecycle a particular dependency is required so that it can be downloaded from the central repository inside the local repository and will be added to the project’s CLASSPATH.

Maven maintains a local repository where it downloads all required plugins and dependencies mentioned in this section.

This local repository Maven creates by default is found at ${use.homer}.m2 epository.

While running particular build phases, Maven checks if the required dependency is available in the local repository. If it is available, it will be added to the project’s CLASSPATH. If it is not available, it will be downloaded from the central repository into the local repository and will be added to the project’s CLASSPATH.

When Maven downloads any dependency in the local repository it creates a directory structure inside the local repository, according to the GroupId, ArtifactId, and Version of the dependency. For this API project, the TestNg dependency has the following settings: GroupId:org.testng, ArtifactId:testing, and Version:7.4.0.

When downloading this dependency, Maven will create the directory structure shown in Figure 11-8 in the local repository.

A diagram has boxes and arrows. From the top, diagonally to the bottom, Org, arrow, test n g, arrow, test n g, arrow, and 7.4.0.

Figure 11-8

The directory structure created by Maven in the local repository

Inside this, the testng-7.4.0.jar file will be downloaded. You can change the default location of the local repository from ${use.homer}.m2 epository to another location by creating a settings.xml file inside ${use.homer}.m2. The settings.xml file should specify a desired local repository location using the <localRepository> tag:
<settings>
      <localRepository>D:MavenRepo</localRepository>
</settings>
  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
Listing 11-4

Maven Project Build Section from pom.xml

The build tag in pom.xml (see Listing 11-4) defines the end-end build lifecycle of an application, along with the Maven plugins used to execute each phase.

Build Lifecycle Phases and Their Order

Clean: Maven uses the maven-clean-plugin, which cleans all previously generated compiled files as well as package files and runs a fresh build lifecycle.

Download resources: In this phase, all the project’s dependencies are downloaded from the artifact repository, like mvnrepository.com.

The Maven plugin downloads project dependencies along with their circular dependencies.

Compilation of application source code: Maven uses the maven-compiler-plugin, which internally uses javac (a Java compiler) to compile the source code of the application. The compiler plugin compiles all the .JAVA files from src/main/java, which is the default location, to find the sources of an application.

This compiler plugin takes the Java files as input and produces .CLASS files as output of this compilation phase. The compilation phase creates .CLASS files for application inside the $(ProjectDIR)/target/classes folder.

Unit Testing: First mvn compile:test-compile phase compiles unit test code from src/test/java and puts the class files inside the $(ProjectDIR)/target/test-classes folder.

Maven uses the Maven-surefire plugin, which triggers unit test cases by invoking JUnit or TestNg frameworks.

Package: Maven-jar-plugin bundles application’s compiled files (i.e. .CLASS files created inside the $(ProjectDIR)/target/classes folder) in a .JAR file. This JAR file is created in the $(ProjectDIR)/target folder.

Release Phase: Maven has an install phase in which it installs the JAR file into Maven’s local repository using maven-install-plugin.

If users of this JAR file are remote, this JAR file should be deployed to a central repository available on the web, such as Mvnrepository.com.

This can be achieved by running deploy goal, whereby Maven uses the maven-deploy-plugin to deploy a JAR file on the central repository so that it can be downloaded and used by developers.

How to Use Maven from the CLI

The previous section explained how to use Maven from Eclipse. As previously mentioned, Maven is a command-line tool so it provides different commands to interact with it. Now you learn how to use Maven using CLI in this section.

Setting Up Maven

Download Maven from the following link given on the Apache Maven website:

https://apachemirror.wuchna.com/maven/maven-3/3.8.1/binaries/apache-maven-3.8.1-bin.zip

Unzip the downloaded ZIP file at your desired location. I unzipped my file to D:MavenInstallation.

Inside the apache-maven-3.8.1in, we have the mvn command file. This is the Maven CLI, which you can use from the command prompt. Add this mvn command to the PATH environment variable and create JAVA_HOME and M2_HOME variables in the Environment Variables section.
  1. 1.

    Go to the Environment Variables section: Type Edit System Environment Variables in the Start menu.

     
  2. 2.

    Select the Edit the System Environment Variables option, which will open the System Properties window.

     
  3. 3.

    Click the Environment Variables button, which will open the Environment Variables window.

     
  4. 4.

    Create a M2_HOME environment variable: Click the New button from the System Variables section (see Figure 11-9).

     

A window has the following, User variables for magic-user, and below it is a table with headers, Variable, and Value. The entries, Name, and Pranoday are highlighted. Below the table, at the right are the tabs, New, Edit, and Delete. Below, a System Variables table is boxed. The entry CLASSPATH, D colon backslash Java Selenium Batch backslash Java Programs, is highlighted. Below it is the tabs, New, which are highlighted, Edit, and Delete. The OK, and Cancel tabs are at the bottom.

Figure 11-9

The Environment Variables window with System Variables section highlighted

The New System Variable window will open.
  1. 5.

    Enter M2_HOME into the Variable Name field and the location of Maven in the Variable Value field (see Figure 11-10).

     

A New System Variable window. Below is, Variable Name, with M 2 underscore HOME typed in, and Variable Value, with D colon backslash Maven Installation backslash apache dash maven dash 3.8.1 typed in. At the bottom, at left, are the Browse Directory, and Browse File tabs. And at right, The O K and Cancel tabs.

Figure 11-10

The Variable Name and Variable Value fields are filled in when creating the M2_HOME environment variable

  1. 6.

    Click the OK button, which will add the M2_HOME entry to the System Variables section in Environment Variables (see Figure 11-11).

     

A window has the following, User variables for magic-user, and below it is a table with headers, Variable, and Value. The entries, Name, and Pranoday are highlighted. Below the table, at the right are the tabs, New, Edit, and Delete. Below is a System Variables table in which the entry, M 2 underscore HOME, D colon backslash Maven Installation Backslash apache dash Maven dash 3.8.1 is highlighted and boxed. Below, the OK button is highlighted.

Figure 11-11

The M2_HOME environment variable

  1. 7.

    Add a bin folder to the Maven folder in the PATH environment variable: Locate the PATH environment variable entry in the Environment Variables list under the System Variables section (see Figure 11-12).

     

A window has the following, User variables for magic-user, and below it is a table with headers, Variable, and Value. The entries, Name, and Pranoday are highlighted. Below the table, at the right are the tabs, New, Edit, and Delete. Below is a System Variables table in which the entry, Path C colon backslash Windows backslash system 32, C colon backslash Windows, C colon backslash Windows backslash System 32 backslash W b is highlighted. Below, the OK tab is highlighted.

Figure 11-12

The PATH environment variable in the System Variables section

  1. 8.

    Click the Edit button. Then click the New button and enter %M2_HOME%in in the newly created entry inside the Edit Environment Variable list (see Figure 11-13).

     

An Edit environment variable window. Below is a table with multiple entries. The entry at the bottom, percentage sign M 2 underscore HOME percentage sign backslash bin is boxed, and the space beside it is colored in. An OK button below is highlighted.

Figure 11-13

The bin folder entry added to the PATH environment variable

  1. 9.

    Click the OK button.

     
  2. 10.

    Create the JAVA_HOME environment variable: Click the New button from the System Variables section.

     
The New System Variable window will open.
  1. 11.

    Enter JAVA_HOME in the Variable Name field and the location of the Java Development Kit (JDK) in the Variable Value field (see Figure 11-14).

     

A New System Variable window. Below are the Variable name, with JAVA underscore HOME typed in, and the Variable value, with D colon backslash j d k dash 9.0.4 backslash j d k dash 9.0.4, typed in. Below left is the Browse Directory, and Browse File tabs. And at the right are the OK and Cancel buttons.

Figure 11-14

The Variable Name and Variable Value fields are filled in when creating the JAVA_HOME environment variable

  1. 12.

    Once all the required configurations are done, check if Maven is configured successfully by running the mvn -version command from the command prompt.

     

Using Maven CLI Commands

Once Maven is set up to be used from the command prompt, you can run different Maven commands to control different build lifecycle phases on the API project.

Open the command prompt and change the working directory to the project directory in the command prompt using the cd DOS command (see Figure 11-15).

A command prompt window with, Microsoft Windows open bracket Version 10.0.19041.1052 close bracket, open parenthesis c close parenthesis backslash Microsoft Corporation. All rights reserved. Below is a code, C colon Users backslash magicuser greater than c d space D colon backslash Jenkins Book Examples backslash Calculator A P I, C colon backslash Users backslash magicuser greater than D colon, D colon backslash Jenkins Book Examples backslash.

Figure 11-15

The working directory has changed to the project directory

Note

In order to run Maven commands, you need to be in the Maven Project directory, which contains the pom.xml file.

To run a particular build phase, run the mvn <Goal Name> command. Let’s create a .JAR file of this API project by running the package phase.

Run the mvn package command from the command prompt.

Once you run any Maven goal, it runs all its previous goals as well, For example, when you run the mvn package, it runs the following goals as well:
  1. 1.

    Downloads project dependencies using the maven-resources plugin.

     
  2. 2.

    Compiles API source code.

     
  3. 3.

    Compiles unit test cases code.

     
  4. 4.

    Runs unit test cases using the maven-surefire-plugin.

     
  5. 5.

    Creates a JAR file using the maven-jar-plugin.

     

This section explained how to use the Maven build tool to automate a build lifecycle of a simple Java API project. The next section explains how to customize default Maven settings using the settings.xml file.

Understanding Maven’s settings.xml File

A Maven build tool works with default settings. For example, the local repository’s default location is ${use.homer}.m2 epository. If you want to change this location to some other folder or change the central repository URL, you can do that using settings.xml.

For example, say you want to change the local repository location and include proxy server settings. To do this, you must create a settings.xml file, as shown in the Listing 11-5, and save it in the ${use.homer}.m2 directory.
<settings>
      <localRepository>D:MavenRepo</localRepository>
      <proxies>
            <proxy>
                  <protocol>http</protocol>
                  <host>10.9.1.1</host>
                  <port>80</port>
            </proxy>
      </proxies>
</settings>
Listing 11-5

Maven settings xml with Customized Local Repository and Proxy Settings

Summary

This chapter explained the typical build lifecycle phases of a Java API project and described how these build lifecycle phases can be automated using Maven. You configured a Maven project in Eclipse and executed Maven commands to perform all build lifecycle phases and create an artifact (.JAR) file. You also learned how default Maven settings can be customized using settings.xml. The next chapter explains how to release this artifact on an artifactory called Nexus by integrating Maven and Nexus. Stay tuned!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.49.190