Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Keep Your Maven Projects Portable Throughout the Build Cycle : Page 2

How portable is your Java project? How many modifications must be made to a build environment in order to produce a successful artifact? Determine your project's portability and learn how Maven can improve it.




Full Text Search: The Key to Better Natural Language Queries for NoSQL in Node.js

Solving Common Portability Problems in Maven

After defining the level of portability you need, the problem becomes moving up to or above that level. If your project is self-contained and your audience can build and run it without any modifications or installations beyond standard Maven, then congratulations! Your project is widely portable; you may now stop reading. However, if your project is non-trivial then please continue.

Avoid System Scopes
In the dependency element, avoid provided and system scopes, such as:

<dependency> <groupId>com.closedsource</groupId> <artifactId>sometool</artifactId> <version>1</version> <scope>system</scope> <systemPath>/usr/lib/sometool.jar</systemPath> </dependency>

If the system scope is unavoidable, you should use a property for systemPath. This allows you to set/alter that property per build environment. The best method, however, is to deploy the dependency to an in-house Maven repository (shown below) and use the dependency as a standard scope. (See "Sidebar 5. Grouping Absolute Values" for an addendum to the previous statement.)

Filter is Your Friend
Do not be shy about using filters. Filters are Maven's way of externalizing property replacement (marked in the build lifecycle as "*-process" phases) into easily portable and configurable properties files. Just add a list of standard Java properties files you wish to filter on in build, like this:

<filters> <filter>datasource.properties</filter> </filters>

In the example project, the "datasource.properties" exists in the base build directory (${buildDir}) and contains the "name=value" pair for the "jdbc.url":


When resources are filtered, the project replaces all matching property names with their corresponding values, taking the filter list into account. The resources to be filtered are defined by the "resources" build element. For example, this block says that your project has XML resources that should be filtered, and that the results will be put into the META-INF directory:

<resources> <resource> <filtering>true</filtering> <directory>src/main/resources</directory> <targetPath>META-INF</targetPath> <includes> <include>*.xml</include> </includes> </resource> </resources>

You can run the example in the sample project by executing the "process-resources" phase in the command line. It will convert the "datasource.xml" resource file from this:

<datasource> <jdbc-url>${jdbc.url}</jdbc-url> </datasource>

Into this (in the "target/META-INF" directory):

<datasource> <jdbc-url>jdbc:driver://localhost/myDB</jdbc-url> </datasource>

Environmental Portability with Profiles

As a general rule, try to make one profile per environment. Unless you are building on more than five or so environment types, I suggest putting the profile in the POM. Then you may activate the desired profile via the -P argument on the command line. You can test which profiles are currently active with the "help" plugin, like this:

mvn help:active-profiles

You can make environmental changes even simpler by utilizing the "settings.xml" file. The example project contains three profiles: env-dev, env-test, and env-prod. If you wish to ensure that the env-test profile is always activated on your test environment, add the activeProfile to that environment's "settings.xml" file, as follows:

<settings> ... <activeProfiles> <activeProfile>env-test</activeProfile> </activeProfiles> </settings>

For every environment-specific project, name the test profile env-test. Maven will simply ignore the "activeProfile" line if it does not exist. No harm, no foul. The sample project contains a "settings.xml" file. Feel free to experiment.

Profiles, Profiles, Everywhere

What if you have many more profiles? Some projects require subtle build alterations for several clients, sometimes hundreds. The thing to do in this scenario is create "profiles.xml" files that may be copied at the necessary build time. A "profiles.xml" file is exactly like a "pom.xml" profiles element, containing one or more profile elements. It is merely externalized. For example, the profile for your first client may be:

<profiles> <profile> <id>client-0001</id> <activation> <activeByDefault>true</activeByDefault> </activation> <properties> <!-- The client loves blue --> <css.pref>blue.css</css.pref> <!-- They want standard pkg --> <module>default</module> </properties> </profile> </profiles>

Create one profile per "profiles.xml" and make it activeByDefault. Its very existence will ensure that the profile is utilized in the build. If the above "profiles.xml" file is in the base build directory, the help:active-profiles goal will print this:

The following profiles are active: - client-0001 (source: profiles.xml)

Use Profiles Not Properties

(This advice is directed toward in-house projects with fixed environments, not a widely portable open source or plugin project.)

A minor task that I often insist upon is creating environment profiles rather than passing in command-line properties, even if the profile consists of only one property. The logic behind this is simple. If you created the following profile for an environment:

<profiles> <profile> <id>env-test</id> <properties> <install.location>/testbox/app</install.location> </properties> </profile> </profiles>

You command-line activation would be this:

mvn install -P env-test

instead of this:

mvn install -Dinstall.location=/user/local/test

Now imagine that you need to add another property. The manual property settings will become longer, while the profiled POM command-line remains fixed, regardless of the number of properties you may add in the future:

mvn install -Dinstall.location=/user/local/test -Dnew.prop=true

This becomes important if you decide to use a continuous integration server like Continuum.

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date