Maven by Example

4.8. Running the Simple Weather Program

Using the Exec plugin from the Codehaus Mojo project, we can run the Main class:

$ mvn install
$ mvn exec:java -Dexec.mainClass=org.sonatype.mavenbook.weather.Main
...
[INFO] [exec:java]
0INFO  YahooRetriever  - Retrieving Weather Data
134  INFO  YahooParser  - Creating XML Reader
333  INFO  YahooParser  - Parsing XML Response
420  INFO  WeatherFormatter  - Formatting Weather Data
*********************************
Current Weather Conditions for:
Evanston, IL, US

Temperature: 45
Condition: Cloudy
Humidity: 76
Wind Chill: 38
*********************************
...

We didn’t supply a command-line argument to the Main class, so we ended up with the default zip code, 60202. To supply a zip code, we would use the -Dexec.args argument and pass in a zip code:

$ mvn exec:java -Dexec.mainClass=org.sonatype.mavenbook.weather.Main \
      -Dexec.args="70112"
...
[INFO] [exec:java]
0INFO  YahooRetriever  - Retrieving Weather Data
134  INFO  YahooParser  - Creating XML Reader
333  INFO  YahooParser  - Parsing XML Response
420  INFO  WeatherFormatter  - Formatting Weather Data
*********************************
Current Weather Conditions for:
New Orleans, LA, US

Temperature: 82
Condition: Fair
Humidity: 71
Wind Chill: 82
*********************************
[INFO] Finished at: Sun Aug 31 09:33:34 CDT 2008
...

As you can see, we’ve successfully executed the simple weather command-line tool, retrieved some data from Yahoo! Weather, parsed the result, and formatted the resulting data with Velocity. We achieved all of this without doing much more than writing our project’s source code and adding some minimal configuration to the pom.xml. Notice that no “build process” was involved. We didn’t need to define how or where the Java compiler compiles our source to bytecode, and we didn’t need to instruct the build system how to locate the bytecode when we executed the example application. All we needed to do to include a few dependencies was locate the appropriate Maven coordinates.

4.8.1. The Maven Exec Plugin

The Exec plugin allows you to execute Java classes and other scripts. It is not a core Maven plugin, but it is available from the Mojo project hosted by Codehaus. For a full description of the Exec plugin, run:

$ mvn help:describe -Dplugin=exec -Dfull

This will list all of the goals that are available in the Maven Exec plugin. The Help plugin will also list all of the valid parameters for the Exec plugin. If you would like to customize the behavior of the Exec plugin you should use the documentation provided by help:describe as a guide. Although the Exec plugin is useful, you shouldn’t rely on it as a way to execute your application outside of running tests during development. For a more robust solution, use the Maven Assembly plugin that is demonstrated in the section Section 4.13, “Building a Packaged Command Line Application”, later in this chapter.

4.8.2. Exploring Your Project Dependencies

The Exec plugin makes it possible for us to run the simplest weather program without having to load the appropriate dependencies into the classpath. In any other build system, we would have to copy all of the program dependencies into some sort of lib/ directory containing a collection of JAR files. Then, we would have to write a simple script that includes our program’s bytecode and all of our dependencies in a classpath. Only then could we run java org.sonatype.mavenbook.weather.Main. The Exec plugin leverages the fact that Maven already knows how to create and manage your classpath and dependencies.

This is convenient, but it’s also nice to know exactly what is being included in your project’s classpath. Although the project depends on a few libraries such as Dom4J, Log4J, Jaxen, and Velocity, it also relies on a few transitive dependencies. If you need to find out what is on the classpath, you can use the Maven Dependency plugin to print out a

$ mvn dependency:resolve
...
[INFO] [dependency:resolve]
[INFO]
[INFO] The following files have been resolved:
[INFO]com.ibm.icu:icu4j:jar:2.6.1 (scope = compile)
[INFO]commons-collections:commons-collections:jar:3.1 (scope = compile)
[INFO]commons-lang:commons-lang:jar:2.1 (scope = compile)
[INFO]dom4j:dom4j:jar:1.6.1 (scope = compile)
[INFO]jaxen:jaxen:jar:1.1.1 (scope = compile)
[INFO]jdom:jdom:jar:1.0 (scope = compile)
[INFO]junit:junit:jar:3.8.1 (scope = test)
[INFO]log4j:log4j:jar:1.2.14 (scope = compile)
[INFO]oro:oro:jar:2.0.8 (scope = compile)
[INFO]velocity:velocity:jar:1.5 (scope = compile)
[INFO]xalan:xalan:jar:2.6.0 (scope = compile)
[INFO]xerces:xercesImpl:jar:2.6.2 (scope = compile)
[INFO]xerces:xmlParserAPIs:jar:2.6.2 (scope = compile)
[INFO]xml-apis:xml-apis:jar:1.0.b2 (scope = compile)
[INFO]xom:xom:jar:1.0 (scope = compile)

As you can see, our project has a very large set of dependencies. While we only included direct dependencies on four libraries, we appear to be depending on 15 dependencies in total. Dom4J depends on Xerces and the XML Parser APIs, Jaxen depends on Xalan being available in the classpath. The Dependency plugin is going to print out the final combination of dependencies under which your project is being compiled. If you would like to know about the entire dependency tree of your project, you can run the dependency:tree goal.

$ mvn dependency:tree
...
[INFO] [dependency:tree]
[INFO] org.sonatype.mavenbook.custom:simple-weather:jar:1.0
[INFO] +- log4j:log4j:jar:1.2.14:compile
[INFO] +- dom4j:dom4j:jar:1.6.1:compile
[INFO] |  \- xml-apis:xml-apis:jar:1.0.b2:compile
[INFO] +- jaxen:jaxen:jar:1.1.1:compile
[INFO] |  +- jdom:jdom:jar:1.0:compile
[INFO] |  +- xerces:xercesImpl:jar:2.6.2:compile
[INFO] |  \- xom:xom:jar:1.0:compile
[INFO] | +- xerces:xmlParserAPIs:jar:2.6.2:compile
[INFO] | +- xalan:xalan:jar:2.6.0:compile
[INFO] | \- com.ibm.icu:icu4j:jar:2.6.1:compile
[INFO] +- velocity:velocity:jar:1.5:compile
[INFO] |  +- commons-collections:commons-collections:jar:3.1:compile
[INFO] |  +- commons-lang:commons-lang:jar:2.1:compile
[INFO] |  \- oro:oro:jar:2.0.8:compile
[INFO] +- org.apache.commons:commons-io:jar:1.3.2:test
[INFO] \- junit:junit:jar:3.8.1:test
...

If you’re truly adventurous or want to see the full dependency trail, including artifacts that were rejected due to conflicts and other reasons, run Maven with the debug flag.

$ mvn install -X
...
[DEBUG] org.sonatype.mavenbook.custom:simple-weather:jar:1.0 (selected for null)
[DEBUG]   log4j:log4j:jar:1.2.14:compile (selected for compile)
[DEBUG]   dom4j:dom4j:jar:1.6.1:compile (selected for compile)
[DEBUG] xml-apis:xml-apis:jar:1.0.b2:compile (selected for compile)
[DEBUG]   jaxen:jaxen:jar:1.1.1:compile (selected for compile)
[DEBUG] jaxen:jaxen:jar:1.1-beta-6:compile (removed - )
[DEBUG] jaxen:jaxen:jar:1.0-FCS:compile (removed - )
[DEBUG] jdom:jdom:jar:1.0:compile (selected for compile)
[DEBUG] xml-apis:xml-apis:jar:1.3.02:compile (removed - nearer: 1.0.b2)
[DEBUG] xerces:xercesImpl:jar:2.6.2:compile (selected for compile)
[DEBUG] xom:xom:jar:1.0:compile (selected for compile)
[DEBUG]   xerces:xmlParserAPIs:jar:2.6.2:compile (selected for compile)
[DEBUG]   xalan:xalan:jar:2.6.0:compile (selected for compile)
[DEBUG]   xml-apis:xml-apis:1.0.b2.
[DEBUG]   com.ibm.icu:icu4j:jar:2.6.1:compile (selected for compile)
[DEBUG]   velocity:velocity:jar:1.5:compile (selected for compile)
[DEBUG] commons-collections:commons-collections:jar:3.1:compile
[DEBUG] commons-lang:commons-lang:jar:2.1:compile (selected for compile)
[DEBUG] oro:oro:jar:2.0.8:compile (selected for compile)
[DEBUG]   junit:junit:jar:3.8.1:test (selected for test)

In the debug output, we see some of the guts of the dependency management system at work. What you see here is the tree of dependencies for this project. Maven is printing out the full Maven coordinates for all of your project’s dependencies and the mechanism at work.