Monday, February 26, 2007

From maven to mvn : Part 2 -- Java 5 & AspectJ

In this morning's Part 1 I walked through last Thursday morning's activities that began my process of converting a simple project from maven 1.0.4 to 2.0.4. This afternoon (actually, after I come back from the meeting I'm about to go off to) I'll run through last Thursday afternoon's activities where I dealt with dependencies, AspectJ and other details to get my application's war file built.

[ time passes ]

OK, I'm back from my meeting and ready to dig into Part 2. My goals for Part 2 are carried over from Part 1:
- compile the classes and aspects
- build a war (not necessarily deployable)
- test cases should execute and pass
- @foo@ replacement is not necessary

As you recall, Part 1 ended with build failures. I was intentionally vague about the details since your details will differ from mine. However, we have to solve the specific case so we will tackle my specific issues. Hopefully this will give you some insight into your own solution.

We start with this noise:

path/to/UserDelete.java:[26,5] annotations are not supported in -source 1.3
(try -source 1.5 to enable annotations)
@Override

Great! I'm using the shiny new Java 5 stuff and maven thinks I'm stuck in dinosaur land. Well, clearly, a lot of people are using m2 to build Java 5 applications so this must be an easy thing to solve.

As it turns out, the solution is documented on the maven site quite clearly. Rather than paste all of that here I'll just point you there. Be sure you specify 1.5 (or whatever comes after that) and you'll be good to go.

Now my compile is marginally better. No complaints about @Foo but lots of complaints because of missing dependencies. Time to address that...

Recall that I'm starting with a project that currently builds under m1. This means that (a) I already know what my dependencies are and (b) I have a copy of them in ~/.maven/repository. By inspecting my previous project.xml (or the /WEB-INF/lib directory of the m1-built jar) I can quickly identify my dependencies.

Again, the maven documentation comes to the rescue. Dependency management is at the heart of what maven does for you. Follow the link to get the word on defining your dependencies.

M2 does this really cool thing called transitive dependency management. What that means is that if you depend on foo and foo depends on bar then you only need to define foo in your pom.xml. This is vastly different from (and much easier than) m1 where you had to define not only your own dependencies but your dependencies' dependencies as well.

At this point in our series I am not going to be that clever. The project.xml from my m1-buildable project lists my entire dependency tree so I'm going to simply replicate that in my pom.xml. The result will be hideous but I'll get smarter later (possibly in Part 3 if I'm lucky).

One thing you will notice is that you no longer need to worry about the <property.../> tags to indicate what is or is not in the final war. (You will notice this because if you copy the entire dependencies section from your project.xml to pom.xml you will get xml violations when you try to compile your project.) M2 is smart enough to figure that out based on the dependency's scope but we'll worry about that later as well.

So, after adding my dependencies (300 lines worth) I can try again to build my application. Unfortunately many of my dependencies are not available in the m2 repository. Truthfully, I probably specified them incorrectly. What I should do is find them in the m2 repository and respecify them with the correct group and application id values. Well, I'm in a hurry and I know I have them in my local m1 repository so I'm not going to do the right thing. Instead, m2 suggests I can do this:

10) ojdbc:ojdbc:jar:1.4

Try downloading the file manually from the project website.

Then, install it using the command:
mvn install:install-file -DgroupId=ojdbc -DartifactId=ojdbc \
-Dversion=1.4 -Dpackaging=jar -Dfile=/path/to/file

Path to dependency:
1) foo.bar.baz:contacts:war:1.0-SNAPSHOT
2) ojdbc:ojdbc:jar:1.4

And that is exactly what I intend to do... For every missing dependency I will locate it in my ~/.maven/repository directory and invoke the magic charm above to put it into my ~/.m2/repository directory. As I stated above, this is not the Right Way to do it but it is expedient. Once I get smarter (by reading more at the maven site) I will apply what I've learned.

Now my dependencies are in place and I can actually build the code! Sadly, however, my unit tests are failing. <sigh/> Well, this is a Spring-based application and that means a pile of configuration xml files. Unsurprisingly, they're not in target/test-classes and that's why our tests are failing.

You can find the answer to resources in the getting started guide. Since I'm coming from an m1 project mine were originally in src/java (now at src/main/java) as well as src/test (src/test/java). M2 seems to want them at src/*/resources but I'm in no mood at this time of day to extrude them from their current location. Therefore... I will use what the GSG has told me and blaze my own path:
<build>
...
<resources>
<resource>
<directory>${basedir}/src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
<testResources>
<testResource>
<directory>${basedir}/src/test/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</testResource>
<testResource>
<directory>${basedir}/src/main/webapp</directory>
<includes>
<include>**/*.xml</include>
</includes>
</testResource>
</testResources>
Are you still with me? We're getting closer. Really. I promise. If we compile now we will find our Spring resources in target/test-classes where they're needed. Our tests are still going to fail, however, because... we are not compiling my AspectJ aspects. Oops...

Maven (both m1 and m2 but more so with m2) is built around plug-ins. It turns out that there is one just for AspectJ. You can read all about it at codehaus. Configuring pom.xml to include it is quite simple:

<build>
<plugins>
...
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<configuration>
<source>1.5</source>
<target>1.5</target>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal> <!-- use this goal to weave all your main classes -->
<goal>test-compile</goal> <!-- use this goal to weave all your test classes -->
</goals>
</execution>
</executions>
</plugin>
</plugins>

(I would like to say that's the last time you will see a pom.xml snippet in this series but that would probably be a lie.)

So, now we have all of that in our pom.xml, the compiler set to 1.5 compliance and our 300 lines of dependency declaration... mvn install reveals... A successful compile, test, package cycle! We now have a war! I doubt very much that it will deploy but, my friends, it is past time for me to go home and that is exactly what I intend to do.

Tomorrow I will begin exploring Part 3. If that goes well and I don't have too many meetings I will endeavor to post that here shortly thereafter. Please be patient, you never quite know what a Tuesday may bring...

No comments: