Chapter 4. Managing the Code

In the previous chapters, we studied how Maven core works and how to write a Maven plugin by implementing custom functionalities. In this chapter, we will talk about some extended Maven concepts and functionalities.

This chapter will show us the following concepts in detail:

  • How build profiles are structured
  • How to customize your build phase to face different environments
  • How to use Maven Assembly Plugin to build customized archives out of your project
  • How to use Maven Site Plugin to create a wiki-style website containing all the information related to the project

Maven build profiles

In the previous chapters, we saw how to configure the build of our project. However, we did not address the problem of build portability in the previous chapters. With build portability, we measure how easily we can port a build configuration across different environments. A nonportable build will need more configurations and hacks compared to a portable build.

Of course, portability is sometimes not entirely possible. Some plugins and some applications' configurations might depend on resources that are related to a specific environment.

In order to address such circumstances and facilitate build portability across environments, Maven introduces the concept of a build profile. A build profile is a set of POM elements that you can optionally activate by overriding the corresponding tags in a POM file. This is the only point in which you need to define environment-specific settings.

Profiles modify the POM file at build time by overriding the POM settings according to the configurations set in the profile. In some ways, the profiles are similar to the mvn –f command, providing maintainability to the POM file.

In the following sections, we will see how profiles are structured, and we will instantiate them in our transportation project.

What is a profile?

Maven allows the definition of profiles in different levels.

A build profile might contain project-specific settings. In this case, it must be defined in the pom.xml file of a single project. Based upon whether we want to centralize all the profiles' information related to a single project, we can also define them in the profile descriptor file contained in the ${basedir}/profiles.xml folder. Profiles must be defined in a specific POM file element, which is named profiles.

As shown in the following snippet, all profiles are identified by a unique ID:

    <profiles>
        <profile>
            <id>profile1-id</id>
            <!-- configurations and other stuff -->
        </profile>
        <profile>
            <id>profile2-id</id>
            <!-- configurations and other stuff -->
        </profile>
    </profiles>

Profiles can also be defined at the user level. In this case, the profiles section, which we saw earlier, will be defined in the ${USER_HOME}/.m2/settings.xml file and will be visible for all the projects in the machine.

Profiles defined at the global level are contained in the ${M2_HOME}/conf/settings.xml file.

Tip

Since profiles can be defined across many layers, Maven provides a specific goal to track the active build profiles of a project. Running mvn active-profiles will show all the active profiles of our project.

The structure of a profile

As we said before, build profiles are designed to port a build configuration across different environments. We also said that this result is achieved by overriding some of the POM settings; but which settings can be overridden? The obvious answer to this question is that we can override almost all the properties that we defined in the POM file.

The following snippet from the book titled Maven: The Complete Reference shows the full structure of the plugins:

    <profiles>
        <profile>
            <id>...</id>
            <activation>...</activation>
            <build>
                <defaultGoal>...</defaultGoal>
                <finalName>...</finalName>
                <resources>...</resources>
                <testResources>...</testResources>
                <plugins>...</plugins>
            </build>
            <reporting>...</reporting>
            <modules>...</modules>
            <dependencies>...</dependencies>
            <dependencyManagement>...</dependencyManagement>
            <distributionManagement>...</distributionManagement>
            <repositories>...</repositories>
            <pluginRepositories>...</pluginRepositories>
            <properties>...</properties>
        </profile>
    </profiles>

As we can see, the profile can override almost all the POM sections. It is possible to customize the build package with different modules and dependencies, and it is also possible to define custom resources and properties to fit in the environment settings. We can customize the database connection, the namespace of a WSDL, or the web.xml configuration of a WAR module.

Smart readers will probably notice that two tags of the preceding code sample do not appear in a normal POM file. The id and activation tags are specific to the profile element. As we said earlier, only the id element can uniquely identify the profile within the project.

The activations element explains when the profile has to be used. The concept of profile activation will be explained in more detail in the following section.

Profile activation

As we explained earlier, profiles are needed to activate environment-specific settings during the build phase. The main implication of this fact is that more than one profile will exist within a single project. The concept of activation of a profile is strictly related to its nature; we might want to enable a specific profile when we build the project for a specific operating system or JDK version, or even by default.

Maven provides several different ways to activate a build profile.

A profile might be set to activate by default. If the activation element contains the activeByDefault tag that is set to true, the profile will be active, unless some other profile in the same POM file is activated:

      <activation>
        <activeByDefault>true</activeByDefault>
      </activation>

Tip

Usage of the activeByDefault flag is discouraged. This flag activates the profile if no other profile is active. This implies that the default activation is inhibited by another profile's activation. When you run a multimodule build, the default activation will miss if a build profile has been activated, even if it has been defined in other modules.

The best practice to obtain default activation consists in relying on the absence of a property:

<activation>
  <property>
    <name>dummy.property.name</name>
  </property>
</activation>

You can just define the property in another profile if you want to inhibit the profile's activation:

<properties>
  <dummy.property.name>dummyValue</dummy.property.name>
</properties>

As hinted before, a build profile can also be activated for a specific operating system or JDK version. In these cases, the specific child elements are os and jdk. As we can see in the following code snippet, the os element allows the user to specify the name, family, version, and architecture of the operating system to activate the profiles. All of these elements are optional.

The following snippet shows the structure of this element for a specific operating system and architecture:

      <activation>
        <os>
          <name>Windows 7</name>
          <version>6.1</version>
          <family>Windows</family>
          <arch>amd64</arch>
        </os>
      </activation>

The jdk element allows you to specify a range of JDK versions to be used. The version's range has to be defined through the version range system. It is possible to exclude a specific version using the exclamation mark, as shown in the following example:

      <activation>
        <jdk>!1.5</jdk>
      </activation>

Other activation methods rely on the presence or lack of a property or file. With the property element, we can specify to activate the build profile if a property exists, if it does not, or if it has a specific value. As in the preceding example, the lack of the property is specified by the use of an exclamation mark. The file element allows us to specify whether a file exists with the specific exists child element of the missing element.

Finally, Maven provides the possibility of manually activating one or more build profiles through the –P option. It is possible to indicate more than one profile with a comma-separated notation, as shown here:

mvn clean package –P profile_id1,profile_id2,!profile_id3

Sample build profiles

Throughout this book, we'll show the use of the build profiles mechanism through our example project. We will specifically write two different profiles to build the transportation-acq-war module into a development environment and on the production environment.

Our build profiles will be identified by the dev and prod IDs. The first profile will be active when a fake property doesn't exist, and the production profile will only activate with a specific set of configurations.

Each of these profiles will define a set of properties containing environmental settings. The classic example of such settings, highlighted in the next code snippet, is the database connection string; this string sets different values for different profiles and allows developers to work avoiding to care about environmental details. Once the build has been set up, Maven will set the correct configuration according to the environment that we are building on.

In the following code snippet, we can see the resulting profiles section of our POM file. In addition to everything we've already covered, we also add some different plugin configurations.

During the development phase, we will run unit tests with a custom configuration to ignore test failures, and in the production phase, we will build a WAR file containing a custom web.xml file:

  <profiles>
    <profile>
      <id>dev</id>
      <activation>
        <property>
          <name>!dev.profile.trigger</name>
        </property>
      </activation>

      <properties>
        <db.driverClass>
          oracle.jdbc.driver.OracleDriver
        </db.driverClass>
        <db.url>jdbc:oracle:thin:@127:0:0:1:1521/DBService</db.url>
        <db.user>dbuser</db.user>
        <db.password>dbuser123</db.password>
      </properties>

      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>2.17</version>

            <configuration>
              <testFailureIgnore>true</testFailureIgnore>
              <includes>
                <include>**/test/java/*.java</include>
              </includes>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
    
    <profile>
      <id>production</id>

      <activation>
        <jdk>[1.6,)</jdk>
        <os>
          <family>Unix</family>
          <arch>amd64</arch>
        </os>
      </activation>

      <properties>
        <db.jndi>jdbc/productionOracle</db.jndi>
        <!—- dev profile inhibition -->
        <dev.profile.trigger>I'm a dummy value</dev.profile.trigger>
      </properties>

      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>

            <configuration>
              <webXml>src/main/webapp/WEB-INF/prod/web.xml</webXml>
            </configuration>
          </plugin>
        </plugins>
      </build>
    </profile>
  </profiles>
..................Content has been hidden....................

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