Defining the project structure with Maven

In this recipe, we will focus on defining, with Maven, the project structure we need for our application.

Getting ready

We will initially create two Eclipse projects: one for the application and one for the components that ZipCloud as a company could share later on with other projects. Take a look at the following image which presents the project components that we are going to build:

Getting ready

The application project cloudstreetmarket-parent will have three modules. Two of them will be packaged as web archives (war): the main web application and the REST API. One of them will be packaged as a jar dependency (cloudstreetmarket-core).

The company-specific project zipcloud-parent will have only one submodule—zipcloud-core, which will be packaged as jar.

How to do it...

The following steps will help us create a Maven parent project:

  1. From Eclipse, navigate to File | New | Other.
  2. A New wizard opens up wherein you can select the type of project within a hierarchy. Then, open the Maven category, select Maven Project, and click on Next.

    The New Maven Project wizard opens as shown in the following screenshot:

    How to do it...
  3. Make sure to check the Create a simple project option. Click on Next.
  4. Fill up the next wizard as follows:
    • edu.zipcloud.cloudstreetmarket as Group Id
    • cloudstreetmarket-parent as Artifact Id
    • 0.0.1-SNAPSHOT as Version
    • pom as Packaging
    • CloudStreetMarket Parent as Name
    • Then, click on the Finish button

    The parent project must appear in the package explorer on the left-hand side of the dashboard.

    How to do it...

    We now have to tell m2eclipse which Java compiler version you plan to use in this project so that it automatically adds the right JRE system library to the submodules we are about to create. This is done through the pom.xml file.

  5. Edit pom.xml file to specify the Java compiler version:
    • Double-click on the pom.xml file. The m2eclipse Overview tab shows up by default. You have to click on the last tab, pom.xml, to access the full XML definition.
    • In this definition, add the following block at the end but still as part of the <project> node. (You can also copy/paste this piece of code from the cloudstreetmarket-parent's pom.xml of the chapter_1 source code):
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <verbose>true</verbose>
                <fork>true</fork>
                <executable>${JAVA_HOME}/bin/javac</executable>
                <compilerVersion>1.8</compilerVersion>
            </configuration>
          </plugin>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.4.2</version>
            <configuration>
              <jvm>${JAVA_HOME}/bin/java</jvm>
              <forkMode>once</forkMode>
             </configuration>
          </plugin>
        </plugins>
      </build>

      Note

      You have probably noticed the maven-surefire-plugin declaration as well. We will review it soon; it allows us to run unit tests during the build.

  6. Now, we will create submodules:

    As submodules of the Parent project, we have seen that we needed one web module to handle and render the site's screens, one web module for the REST API, and one other module that will be used to package all the business logic (services, data access, and so on.) specific to the first product cloudstreetmarket.com:

    1. From the main Webapp module:in Eclipse, navigate to File | New | Other. A New wizard opens up through which you can select the type of project within a hierarchy. Open the Maven category, select Maven Module, and click on Next.
    2. The New Maven Module wizard opens up after this; fill it up as follows:

      Check Create a simple project.

      Enter cloudstreetmarket-webapp as Module Name.

      Enter cloudstreetmarket-parent as Parent project.

      How to do it...
    3. Click on the Next button after which the next step shows up. Enter the following entries in that new window:

      Enter edu.zipcloud.cloudstreetmarket as Group Id.

      Enter 0.0.1-SNAPSHOT as Version.

      Select war as Packaging.

      Enter CloudStreetMarket Webapp as Name.

      Then click on the Finish button.

  7. Now we will go ahead to create and REST API module:

    We are going to repeat the previous operation with different parameters.

    1. From Eclipse, navigate to File | New | Other. The selection wizard pops up when you go there. After this, open the Maven category, select Maven Module, and click on Next:
    2. In the New Maven Module wizard, enter the following entries:

      Check the Create a simple project option.

      Enter cloudstreetmarket-api as Module Name.

      Enter cloudstreetmarket-parent as Parent project.

    3. Click on the Next button to proceed to the next step. Enter the following entries in that window:

      Enter edu.zipcloud.cloudstreetmarket as Group Id.

      Enter 0.0.1-SNAPSHOT as Version.

      Select war as Packaging.

      Enter CloudStreetMarket API as Name.

      Then click on the Finish button.

  8. Now, we will create the core module:

    For this, navigate to File | New | Other. The selection wizard pops up when you do so. Open the Maven category, select Maven Module, and click on Next.

    1. In the New Maven Module wizard, enter the following entries:

      Check the Create a simple project option.

      Enter cloudstreetmarket-core as Module Name.

      Enter cloudstreetmarket-parent as Parent project.

    2. Click on the Next button to go to the next step. Fill in the fields with the following:

      Enter edu.zipcloud.cloudstreetmarket as Group Id.

      Enter 0.0.1-SNAPSHOT as Version.

      This time, select jar as Packaging.

      Enter CloudStreetMarket Core as Name.

      Then click on the Finish button.

    If you have the Java perspective activated (in the top-right corner), you should see the overall created structure matching the screenshot here:

    How to do it...
  9. Now, we will create a company-specific project and its module(s):

    Let's assume that many different categories of dependencies (core, messaging, reporting, and so on…) will be part of the company-business project later.

    1. We need a parent project, so from Eclipse, navigate to File | New | Other. The selection wizard pops up. Open the Maven category, select Maven Project, and click on Next.
    2. In the first step of the New Maven Project wizard, as for the Parent project we created earlier, only check the Create a simple Project and Use default workspace location options.
    3. Click on the Next button and fill in the next wizard as follows:

      Enter edu.zipcloud as Group Id.

      Enter zipcloud-parent as Artifact Id.

      Enter 0.0.1-SNAPSHOT as Version.

      Select pom as Packaging.

      Enter ZipCloud Factory Business Parent as Name.

    Again, in the created pom.xml file, add the following block inside the <project> node to create the underlying modules properly and to enable automatic test execution. (You can also copy/paste this piece of code from the zipcloud-parent's pom.xml file of the chapter_1 source code):

    <build>
      <plugins>
        <plugin>
        <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.1</version>
          <configuration>
            <source>1.8</source>
            <target>1.8</target>
              <verbose>true</verbose>
              <fork>true</fork>
            <executable>${JAVA_HOME}/bin/javac</executable>
          <compilerVersion>1.8</compilerVersion>
          </configuration>
        </plugin>
        <plugin>
        <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-surefire-plugin</artifactId>
            <version>2.4.2</version>
            <configuration>
            <jvm>${JAVA_HOME}/bin/java</jvm>
            <forkMode>once</forkMode>
          </configuration>
        </plugin>
      </plugins>
    </build>

    Now, we are going to create one company-business core module, which will be a sub module of the parent project we just created.

    For this, navigate to File | New | Other. The selection wizard pops up. Open the Maven category, select Maven Module, and click on Next.

    1. In the New Maven Module wizard, enter the following details:

      Check the Create a simple project option.

      Enter zipcloud-core as Module Name.

      Enter zipcloud-parent as Parent project.

    2. Click on the Next button and go to the next step. Here, enter the following details:

      Enter edu.zipcloud as Group Id.

      Enter 0.0.1-SNAPSHOT as Version.

      Select jar as Packaging.

      Select ZipCloud Factory Core Business as Name.

  10. Now, build the two projects:

    If the structure is correct, the following Maven command could be successfully run:

    mvn clean install
    

    Tip

    This command can be launched in the terminal if Maven is installed on the development machine.

    In our study case, we will, for now, launch it using the m2eclipse modified Run As menu: Right click on the zipcloud-parent project and click on Run As | Maven Clean.

    Note

    In the Maven console, you should now see this beautiful line at the bottom:

    [INFO] BUILD SUCCESS

    Now, repeat the operation for the install build phase. You should now see the following output in the console:

    [INFO] ZipCloud Parent .......................SUCCESS [  0.313 s]
    [INFO] ZipCloud Core .........................SUCCESS [  1.100 s]
    [INFO] ----------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ----------------------------------------------------------
    

    Ok, now you should be able to build cloudstreetmarket-parent as well.

    For this, right-click on the cloudstreetmarket -parent project and click on Run As | Maven Clean. The Maven console should print the following after this step:

    [INFO] BUILD SUCCESS
    

    Again, right-click on the cloudstreetmarket -parent project and click on Run As | Maven Install. The Maven console should now print the following:

    [INFO] CloudStreetMarket Parent ..............SUCCESS [  0.313 s]
    [INFO] CloudStreetMarket Webapp ..............SUCCESS [  6.129 s]
    [INFO] CloudStreetMarket Core ................SUCCESS [  0.922 s]
    [INFO] CloudStreetMarket API .................SUCCESS [  7.163 s]
    [INFO] ----------------------------------------------------------
    [INFO] BUILD SUCCESS
    [INFO] ----------------------------------------------------------
    

    Scrolling up a bit should display the following trace:

    -------------------------------------------------------
     T E S T S
    -------------------------------------------------------
    There are no tests to run.
    Results :
    Tests run: 0, Failures: 0, Errors: 0, Skipped: 0
    

    Note

    Maven here, with the help of the maven-surefire-plugin, which we manually added, parses all the classes encountered in the src/test/java directories. Again, this path can be customized.

    In the detected test classes, Maven will also run the methods annotated with the JUnit @Test annotation. A JUnit dependency is required in the project.

How it works...

In this section, we are going through quite a few concepts about Maven so that you can better understand its standards.

New Maven project, new Maven module

The project creation screens we just went through also come from the m2eclipse plugin. These screens are used to initialize a Java project with a preconfigured pom.xml file and a standard directory structure.

The m2eclipse plugin also provides a set of shortcuts to run Maven build phases and some handy tabs (already seen) to manage project dependencies and visualize the pom.xml configuration.

The standard project hierarchy

Navigating through the created projects, you should be able to notice a recurring hierarchy made of the following directories: src/main/java, src/main/resource, src/test/java, and src/test/resource. This structure is the default structure that Maven drives us through. This model has become a standard nowadays. But, we can still override it (in the pom.xml files) and create our own hierarchy.

If you remember the maven-compiler-plugin definition added in the pom.xml files of the parent projects, there were the following four lines of code that we used:

<verbose>true</verbose>
<fork>true</fork>
<executable>${JAVA_HOME}/bin/javac</executable>
<compilerVersion>1.8</compilerVersion>

These lines allow Maven to use an external JDK for the compiler. It is better to have control over which compiler Maven uses, especially when managing different environments.

Also, there were the following two lines that might look like an over configuration:

<source>1.8</source>
<target>1.8</target>

From a strict Maven point of view, these lines are optional when an external JDK is defined with a specified compilerVersion. Initially, with these two lines, we can control which Java version we want the default code to be compiled in. When maintaining older systems, the existing code might still compile in a previous version of Java.

Actually, m2eclipse specifically expects these two lines in order to add JRE System Library [JavaSE-1.8] to the build path of the jar and war modules. Now, with these lines, Eclipse compiles these projects in the same way Maven does: in Java SE 8.

Tip

If this dependency still shows up as a different version of Java, you may need to right-click on the module and then navigate to Maven | Update Project.

The project's structure in the IDE

About the parent projects in the Eclipse project hierarchy; did you notice that the created submodules seem duplicated as standalone projects and as direct children of the parent? This is due to the fact that Eclipse doesn't handle hierarchies of projects yet in Luna. For this reason, the modules appear as separated projects. It might be slightly confusing because the source code appears to be located beside the parent projects. This is not the case in reality, it is only the way they are rendered, so we can have all the tools normally bound to the project level.

Note

At this time, JetBRAINS IntelliJ IDEA already supports visual hierarchies of the projects.

Finally, if you open a parent project's pom.xml file, you should see the <modules> node populated with the created submodules. This has been done automatically as well by m2eclipse. We recommend that you keep an eye on this feature because m2eclipse doesn't always update these <modules> nodes depending on which way you alter the project hierarchy.

Maven's build life cycles

A build life cycle in Maven is a specific sequence (and a group) of predefined operations called phases. There are three existing life cycles in Maven: default, clean, and site.

Let's have a look at all the phases that include the default and clean life cycles (probably the life cycles the most commonly used by developers).

The clean life cycle

The Maven clean phase plays a central role. It resets a project build from Maven's perspective. It is usually about deleting the target directory that is created by Maven during the build process. Here are some details about the phases included in the clean life cycle. These details come from the Maven documentation:

Phases

Description

pre-clean

This executes processes that are needed prior to the actual project cleaning

clean

This removes all files generated by the previous build

post-clean

This executes processes that are needed to finalize the project cleaning

The default life cycle

In the default life cycle, you can find the most interesting build phases that deal with source generation, compilation, resource handling, tests, integration tests, and artefact deployment. Here are some details about the phases included in the default life cycle:

Phases

Descriptions

validate

This validates whether the project is correct and all necessary information is available.

initialize

This initializes the build state, for example, setting properties or creating directories.

generate-sources

This generates source code for inclusion in compilation.

process-sources

This processes the source code, for example, to filter any values.

generate-resources

This generates resources to be included in the package.

process-resources

This copies and processes the resources into the destination directory, which is ready for packaging.

compile

This compiles the source code of the project.

process-classes

This post processes the generated files from compilation, for example, to perform bytecode enhancement on Java classes.

generate-test-sources

This generates any test source code to be included in compilation.

process-test-sources

This processes the test source code, for example, to filter any values.

generate-test-resources

This creates resources for testing.

process-test-resources

This copies and processes the resources into the test destination directory.

test-compile

This compiles the test source code into the test destination directory.

process-test-classes

This post processes the generated files from test compilation, for example, to perform bytecode enhancement on Java classes. For Maven 2.0.5 and above.

test

This runs tests using a suitable unit testing framework. These tests should not require the code to be packaged or deployed.

prepare-package

This performs the operations necessary to prepare a package before the actual packaging. This often results in an unpacked, processed version of the package. (Maven 2.1 and above)

package

This takes the compiled code and packages it in its distributable format, such as JAR.

pre-integration-test

This performs actions required before integration tests are executed. This may involve things such as setting up the required environment.

integration-test

This processes and deploys the package if necessary into an environment where integration tests can be run.

post-integration-test

This performs the actions required after integration tests have been executed. This may include cleaning up the environment.

verify

This runs checks to verify that the package is valid and meets the quality criteria.

install

This installs the package into the local repository to be used as a dependency in other projects locally.

deploy

This copies the final package to the remote repository to share it with other developers and projects (done in an integration or release environment).

Plugin goals

With the concept of plugins, Maven acquires a much wider dimension. Maven natively provides built-in plugins, but external plugins can be introduced just as other dependencies (identified by groupIds and artefactIds).

Each build phase can be attached to zero, one, or more plugin goals. A goal represents a specific and concrete task responsible for building or handling a project in a certain manner. Some phases have goals bound to them, by default, through native plugins.

Built-in life cycle bindings

Now that we have seen the purpose of each phase in the presented two life cycles, we must say that, for the default life cycle, depending upon which module packaging type we are choosing, only some of these phases are potentially activated for goal execution.

Let's see the phases that we skipped in the default life cycle for different packaging types:

 

Default life cycle

Packaging type

jar/war/ejb/ejb3/rar

ear

maven-plugin

pom

Activated phases

 

generate-resources

generate-resources

 

process-resources

process-resources

process-resources

 

compile

 

compile

 

process-test-resources

 

process-test-resources

 

test-compile

 

test-compile

 

test

 

test

 

package

package

package

package

install

install

install

install

deploy

deploy

deploy

deploy

Tip

In Chapter 9, Testing and Troubleshooting, we will practically bind external plugins goals to identified build phases.

In summary, calling: mvn clean install on a jar packaged-module will result in executing the following phases: clean, process-resources, compile, process-test-resources, test-compile, test, package, and install.

About Maven commands

When Maven is told to execute one or more phases targeting a specific project's pom.xml file, it will execute the requested phase(s) for each of its modules.

Then, for every single requested phase, Maven will do the following:

  • Identify which life cycle the phase belongs to
  • Look for the packaging of the current module and identify the right life cycle binding
  • Execute all the phases in the hierarchy of the identified life cycle bindings, which are located before the requested phase in the hierarchy

Note

By the term execute all the phases, we mean execute all the underlying detected and attached plugin goals (native plugins or not).

In summary, calling mvn clean install on a jar packaged module will execute the following phases: clean, process-resources, compile, process-test-resources, test-compile, test, package, and install.

There's more...

You may wonder why we have created these projects and modules in regard to our application.

How did we choose the jar module's name?

About the Maven structure, the best names for nondeployable modules often emphasize a functional purpose, a specific concept created by the business, or are driven by the product (cloudstreetmarket-chat, cloudstreetmarket-reporting, cloudstreetmarket-user-management, and so on.). This strategy makes the dependency management easier because we can infer whether a new module requires another module or not. Thinking about controllers, services, and DAO layers at a macro scale doesn't really make sense at this stage, and it could lead to design interference or circular dependencies. These technical subcomponents (service, DAO, and so on) will be present or not, as needed, in each functional module as Java packages but not as JAR-packaged dependencies.

How did we choose the names for deployable modules?

Choosing a name for a deployable module (war) is a bit different different from choosing a name for a JAR-packaged module. The deployable archive must be thought of as scalable and potentially load balanced. It is fair to assume that the requests that will target the application to retrieve HTML contents can be distinguished from the ones that will return REST contents.

With this assumption, in our case it has been our wish to split the war into two. Doing so may raise the question of how the web sessions are maintained between the two webapps. We will answer this point later on.

Why did we create core modules?

We created the core modules, firstly, because it is certain that, in the cloudstreetmarket application and also in the company-shared project, we will have POJOs, exceptions, constants, enums, and some services that will be used horizontally by almost all the modules or applications. If a concept is specific to a created functional module, it must not be part of core modules.

Then, it is probably better to start big grained to refine later rather than thinking about modules that may be implemented differently or even not implemented at all. In our case, we are a start-up, and it is not silly to say that the 5 to 10 features we are going to implement can constitute the core business of this application.

See also...

  • We also recommend that you install Code Style Formatters. Triggered from the Save Event, we have, with these formatters, the ability to restyle our code automatically with a uniform predefinition. Having such formatters in a team is much appreciated since it guarantees the same rendering while comparing two files with a versioning tool.
..................Content has been hidden....................

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