Rob Gordon is a seasoned Java developer and a big fan of open source. Oddjob is his own open source project to make job scheduling and task automation in Java much much easier. Rob is based in London. Rob is a DZone MVB and is not an employee of DZone and has posted 17 posts at DZone. You can read more from them at their website. View Full User Profile

A Detour on the Road to Maven

04.10.2012
| 2610 views |
  • submit to reddit

I’ve been progressing quite nicely along my Road to Maven, that was until today when I tried to take a detour.

My preferred way of launching a Java application is to build a class path using a lib/*.jar file matcher. Maven makes setting up my lib directory with all the project’s dependent jars very easy by using the Maven Dependency Plugin:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-dependency-plugin</artifactId>
  <executions>
    <execution>
      <id>copy-to-lib</id>
      <phase>package</phase>
      <goals>
        <goal>copy-dependencies</goal>
      </goals>
      <configuration>
        <outputDirectory>${lib.dir}</outputDirectory>
        <includeScope>compile</includeScope>
        <includeScope>runtime</includeScope>
      </configuration>
    </execution>
  </executions>
</plugin>

In collaboration with the Maven Ant Plugin that copies the application jar:

<plugin>
  <artifactId>maven-antrun-plugin</artifactId>
  <executions>
    <execution>
      <id>copy-jar</id>
      <phase>package</phase>
      <goals>
        <goal>run</goal>
      </goals>
      <configuration>
        <tasks>
          <copy
            file="${project.build.directory}/${project.build.finalName}.jar"
            toDir="${lib.dir}" />
        </tasks>
      </configuration>
    </execution>
    <execution>
      <id>clean</id>
      <phase>clean</phase>
      <goals>
        <goal>run</goal>
      </goals>
      <configuration>
        <tasks>
          <delete dir="${lib.dir}" />
        </tasks>
      </configuration>
    </execution>
  </executions>
</plugin>

(and also performs the clean)

This worked great and I was able to run my application after each mvn package command.

I’m using Eclipse and m2e and I decided I could save some time by changing the Eclipse Default Output Folder to lib/myapp.jar (yes a folder, not a jar, called myapp.jar), and this would save the need to run Maven after every code change that I wanted to see in my application.

Unfortunately changing the output folder was a short cut too far for m2e and it punished me by removing my application classes from the class path so I could no longer run any tests from Eclipse. It took a frustrating hour of trying things before I happened across the answer in a Jira ticket. m2e ignores the eclipse setting for Output Folder when building a Launch Configuration and uses the default target/classes folder regardless.

I don’t need m2e! I’ll just use a standard Eclipse project and add the contents of my lib directory manually to the class path. Of course I’d forgotten the tests. What I need, said my Ant brain, is a target with two tasks. One that copies the compile and runtime scope dependencies to the lib directory and another that copies the test scope dependencies to a test/lib directory.

One goal – two configurations in Maven? Maven says no. The only way is to use profiles. It looks like this.

<profiles>
  <profile>
    <id>copy-to-lib</id>
    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-dependency-plugin</artifactId>
          <configuration>
            <outputDirectory>${lib.dir}</outputDirectory>
            <includeScope>compile</includeScope>
            <includeScope>runtime</includeScope>
          </configuration>
        </plugin>
      </plugins>
    </build>
  </profile>
  <profile>
    <id>copy-to-test</id>
    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-dependency-plugin</artifactId>
          <configuration>
            <outputDirectory>${test.lib.dir}</outputDirectory>
            <includeScope>test</includeScope>
          </configuration>
        </plugin>
      </plugins>
    </build>
  </profile>
</profiles>

Which are run with a mvn dependency:copy-dependencies -Pcopy-to-lib followed by a mvn dependency:copy-dependencies -Pcopy-to-test.

So far so good, except that the copy-to-test copies too much because of transitive dependencies and with <excludeTransitive>true</excludeTransitive> it copies too little!

By now I’ve spent most of the afternoon lost in the Maven jungle and I just need to cut my losses and get back on track. Back on the road again. In a few minutes I’d changed my application class path to be target/classes:target/lib/*.jar and I’d gone back to m2e by deleting the eclipse project (but not the source) and the .classpath and .project files and used the File -> Import -> ExistingMavenProject. Safe again.

I’m learning that Convention over Configuration means conforming, and conforming means you have to learn the rules. Ignorance is no excuse in the eyes of the Maven. Leave the road and you will be punished.

 

 

 

Published at DZone with permission of Rob Gordon, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Geoffrey De Smet replied on Thu, 2012/04/12 - 2:18am

You can configure multiple runs of the maven-dependency-plugin by defining <executions> and a using different <configuration> for each <execution>

Rob Gordon replied on Thu, 2012/04/12 - 5:53am in response to: Geoffrey De Smet

Doesn't that work only when tying the configurations to different phases? If there's another way then could you point me at an example please. Thanks.

Mladen Girazovski replied on Thu, 2012/04/12 - 8:08am

Make an extra module for the packaging of the whole application, that way no need for complex IDE setups or the ant-run plugin anymore, instead you'll use the dependency plugin for copying your jar.

Ricky Clarkson replied on Fri, 2012/04/13 - 11:06pm

Perhaps I'm missing something but it looks like Eclipse is your problem, not maven.  I'd suggest always blaming your IDE when your software builds and works but the IDE gets confused.  I've been mavenising some old projects recently without changing their structure, and neither Maven nor IntelliJ IDEA got confused.

Viktor Nordling replied on Mon, 2012/04/16 - 1:21am

I think you want to have a look at the appassembler plugin: 

http://mojo.codehaus.org/appassembler/appassembler-maven-plugin/

It "assembles the artifacts and generates bin scripts for the configured applications." and puts all the jar files in a lib folder.

The scripts are unfortunately not chmodded for execution, i ended up doing that with the assembly plugin (as per http://jira.codehaus.org/browse/MAPPASM-59).

Good luck! 

 

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.