Tuesday, February 27, 2007

From maven to mvn : Part 3 -- Filtering Resources & Timestamps

Good morning all!

We left off yesterday with an application that will build and tests that will pass. We even have a war file sitting in our target directory. Before I try to deploy that, however, there are some loose ends I want to clean up.

Our goals for Part 3 are:
- @foo@ (or ${foo}) replacement
- attempt to deploy the war

M1 grew out of a set of ant build scripts. As such, filter-when-copying worked pretty much like you would expect from ant. In other words, any @foo@ text in a filter-when-copying resource would be replaced by the foo property value. M2 uses the newer ${foo} syntax and that's probably a good thing.

The first thing I want to do is identify the files I need to change:
  grep -r '@.*@' src/ | grep -v /.svn | grep -v /images/

It turns out that I'm only using a few properties. One no-brainer is the application version that comes from the pom. The others are application specific properties I need to deal with.

So, @pom.artifactId@ should simply become ${pom.artifactId}. Enabling filters is easy enough to do by adding <filtering>true</filtering> to the appropriate resource tag(s).

That gets me very close but not quite done... You see, I'm filtering /WEB-INF/geronimo-web.xml. The one that gets copied into target/test-classes (because of my pom's testResources as discussed in Part 2) is fine. However, the one in target/contacts-1.0-SNAPSHOT (which is managed by the war plugin) is not filtered. Hrm...

It turns out that the war plugin is responsible for managing that bit of the project. Well, that makes sense. So, to change it's behavior we need to configure the plugin:

<build>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<webResources>
<resource>
<directory>${basedir}/src/main/webapp</directory>
<filtering>true</filtering>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</webResources>
</configuration>
</plugin>
Now if you read the plugin documentation about this topic you will probably reach the conclusion that I'm doing it wrong. That is probably the correct conclusion. What I should do is create a ${basedir}/otherResources directory and put the to-be-filtered things there. Well, as with my dependency ick, I'm simply going to defer that to later. I still haven't quite decided for myself whether I like the idea of separating the resources from everything else anyway...

So, now I have my ${pom.*} properties filtered but I still have my application-specific properties to contend with. In particular, I have a myApp.timestamp property supplied by the venerable ant <tstamp/> tag that I slam into footer.jsp to display the build time (very handy for our QA folks.) It turns out that getting the timestamp (oh so easy with m1) is a bit of a trick with m2. Thanks to some good folks on the forum for pointing out a clever use of the build number plugin.

First -- tell maven where to find the plugin:
<pluginRepositories>
<pluginRepository>
<id>tlc</id>
<name>TLC Repository</name>
<url>http://commons.ucalgary.ca/pub/m2</url>
</pluginRepository>
</pluginRepositories>

Second -- configure the plugin to provide a timestamp:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>maven-buildnumber-plugin</artifactId>
<version>0.9.4</version>
<configuration>
<format>{0,date,yyyy-MM-dd HH:mm:ss}</format>
<items>
<item>timestamp</item>
</items>
<doCheck>false</doCheck>
<doUpdate>false</doUpdate>
</configuration>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>create</goal>
</goals>
</execution>
</executions>
</plugin>
In my case I also have to add an <include...> to my filtering since I want my timestamp in footer.jsp. With all of that in place I now get the date/time in my footer.jsp via ${buildNumber}. It isn't nearly so easy or flexible as with m1 but right now I'm interested in results, not cleverness. Perhaps I'll find something better in the future. (Or maybe write my own timestamp plugin!)

(BTW: If you have simple, static properties they are very easy to define in an external file or directly in pom.xml as described by the link above.)

OK, so now I'm filtering and building and testing and packaging so I should have a deployable war. I'm not brave enough to try to deploy via m2 (perhaps another day) so I'll fire up my application server of choice and deploy the war manually. Nothing really to write about here. You probably don't care. I just thought you would like to know that it actually deploys correctly.

You will have to excuse me now; there are some other, non-maven things I need to address this afternoon. (Yes, it is afternoon now. What began in the morning has bled over the international lunch line due to the usual distractions.) If my meeting schedule allows I'll pickup Part 4 tomorrow and try to do a bit of cleanup on my dependencies.

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...

From maven to mvn : Part 1 -- Building a war

I've been a huge fan of maven since the 0.4 days when it was a pile of reusable ant scripts. I encourage any and all Java developers I come across to convert their projects and use it. However, I'm not going to try to convince you here that you need to use maven. If you do then you're already on the path. If you don't then, well, there are other sites that will attempt to convince you. What I intend to do here is describe my journey from maven 1.0.4 to maven 2.0.4.

For a long time now (and I say "long time" because I really have no sense of time) I've been using a modified version of maven 1.0.4. Modified in the sense that I've tweaked a few of the built-in plug-ins, added my own plug-ins and also written a fair amount of value-add in maven.xml. I also want to point out that we use this as the core build framework where I work. Prior to 1.0.4 we used, of course, 1.0.3 and prior to that was some other 1.x. In other words, we've been using some version of maven for a Very Long Time.

I've been following the maven 2 (henceforth simply "m2") effort since its inception. They fine folks there have made some huge improvements. IMO m2 is so very different from m1 (maven 1.x that is) that it is very nearly should have been called something other than "maven". That may be a bit extreme but I simply want to point out that migrating from maven 1.x to 2.x is not a drop-in scenario.

For quite some time now (where "quite some time" is something less than "long time") I've been pondering conversion to m2. I've had a few false starts where a plug-in I needed wasn't there. I've also had a pretty busy schedule such that I just couldn't dedicate the time to learn m2 well enough to give it a fair shot. Fortunately, some other folks I work with have had a bit of time and have begun their own effort to move to m2.

So, with that being said, this series will track my efforts to convert a very simple web application ("Contacts") from m1 to m2. While simple, the project does have some requirements that do not fit m2's out of the box behavior. My theory is that if I can build this application I can probably then convert our core m1-based build framework to m2.

In this episode my goals are:
- compile the classes and aspects
- build a war (not necessarily deployable)
- test cases should execute and pass
- @foo@ replacement is not necessary

Step 1

Build, test and deploy Contacts using the current m1-based build system. I want to be sure that what I think is working is actually working.

Step 2

The application has several components (sub-projects in maven-speak). For this bit I will focus only on the war. Therefore, copy Contacts/view to Contacts-m2/view. Subsequently clean up Contacts-m2/view to get rid of m1 artifacts (maven.xml, etc.)

mkdir -p Contacts-m2/view/
cp --recursive Contacts/view/* Contacts-m2/view/
cd Contacts-m2 ; rm -f maven.* project.*


Step 3

The project layout has changed. Therefore we have to do some simple relocation:

mkdir src/main
mv src/java src/main/java
mv src/aj src/main/aspect
mv src/webapp src/main/webapp
mv src/test src/test-tmp
mkdir src/test
mv src/test-tmp/src/test/java


Step 4

Now we need to have some m2 artifacts so that m2 knows what to do with all of these things. Specifically, we need a pom.xml that describes our project and how to build it.

The easiest way to bootstrap this is to use m2's archetype system:

mvn archetype:create -DgroupId=com.mycompany.app -DartifactId=my-app


Step 5

Well, Step 4 didn't go so well because we're behind a corporate firewall. To deal with this we need to tell m2 how to deal with the proxy. So now we have a ~/.m2/settings.xml file. We may have to come back to this later but we're good for now.

Try the archetype again... all good. It will take a while since m2 needs to pull dependencies for its plug-ins.

Step 6

Now that we have the archetype we will take its pom.xml and throw everything else away.

mv my-app/pom.xml .
rm-rf my-app

The changes to pom.xml will eventually be significant. For now, we just need to change the groupId, artifactId, name and packaging tags. The first three are whatever you need for your application. packaging will be war since we're building one of those.

Note that we could have used the webapp archetype. I really wanted to start with the basics, though, so I went with jar.

Step 6

Now lets build it and see what happens! It's magic, right? Everything should Just Work?

Start with mvn clean just to make sure we didn't flub the pom.xml. There's nothing to clean, of course, but it gives us a good feeling because it cannot possibly fail!

Next try mvn install to build, test and package the application. Ah well... success was short-lived. My application uses Java 5 stuff, AspectJ and a pile of dependencies. With the default pom.xml this build isn't going to complete.

Conclusion

Thus concludes Part 1. I didn't complete all of my goals but i did make some significant progress.

Actual execution took place over a four-hour period during which I was also dealing with my inbox and the inevitable hourly "emergencies" of software development. During the that window of time I probably spent between 90 and 120 minutes doing the things documented above. (The actual documentation -- four days later -- took about 45 minutes.)

I'm off to lunch now. When I get back I'll proof-read this post and document Part 2 where I sort out the build failures in Step 6.

Sunday, February 25, 2007

Specialization

pteropus is something of a stream of consciousness for me. Ideas come to me and some of them come out here. My ideas tend to fall into four broad categories: Christianity, Java geek stuff, other geek stuff, everything else.

For a while now I've been focusing some of my thoughts on my faith into a dedicated blog. I mention it now because I haven't mentioned it before. I struggled a bit with the decision to create the dedicated blog because I'm proud of my faith and want to put it "out there" for all to see. As such, you'll still see some of those thoughts here; thoughts much along the lines of what I've already posted. If you're one of my geek readers I hope you find something of interest in these posts. If so I invite you to visit John-14-6.

On the theory that I may someday feel the need to fire up other specialized blogs I've setup a focal point for my blog-related interests.

Thanks for visiting, come again soon and invite your friends.

Thursday, February 22, 2007

Information Overload

I'm not the first one to notice this and I won't be the last but here it is: We are overloaded with the amount of information we attempt to process each and every day. I'll even narrow it down and say we're overloaded with just the number of websites we attempt to consume every day. Never mind the shows stacking up on TiVo or the Cd's we got at Christmas and haven't listened to yet. And you can simply forget the dead-tree-ware (books, that is) that were given to us on our birthdays months and months ago! At latest count I have over 50 blogs I monitor every day. It's no wonder I rarely have time to update this one!

So what are we going to do about it?

Well, the naive would say that we simply cut back. Drop my 50 to 5 and be done with it. OK, fine, but the problem with that is I've spent a lot of time and energy to be one of best Java programmer (and C++ programmer before that) I can possibly be. I have a passion for what I do and if I'm going to bother to do it then I want to be good at it. To do that means that I need to keep up with the technology and all that's new. After all, you don't get better by doing the same thing the same way all the time. You get better by learning from folks who know things you don't know. To do that means reading lots and lots of stuff put out by people who are a lot smarter than I am. And that sets me up for the information overload.

So, again, what are we going to do about it?

Well, these days any website (be it blog or other) worth visiting has some kind of feed available. Even my own poor blog here has an atom feed and I'm certainly not in the top set of sites to visit! (Actually, I'm not sure that anyone visits at all. Maybe I'm just typing in a vacuum...)

Armed with some sort of site feed we can begin to leverage some pretty cool tools called aggregators. If you don't know what I'm talking about go ask Wikipedia. From my limited investigation they fall into two broad categories: local installables and web-based. (Of course I guess you could say that most applications these days fall into those categories...) My daily digital life involves three to six computers so a local install is not really useful thus I've begun to look into the online variety.

My first attempt to fence in my mass of feeds was Google's customized home page. If you're not doing this then you're missing the boat. And not just any boat. I mean the boat. Google's customized home page is what every ISP or portal-thing vendor has been trying to do since the www got started. There are hundreds, nay, thousands of widgets you can drop on there to customize your web experience. Beyond that, anything with a feed can be dropped on just as easily. Soon, however, I reached a critical mass where I had a half-dozen customized tabs with ten or twenty widgets each and things were beginning to be ignored.

My new favorite toy is Google Reader. I played with it a year or more ago when, I think, it went by the name Google Lens. I'm probably wrong about that name because every time I say it nobody knows what I'm talking about. Whatever you name it, it's cool.

Now I'm not going to turn this into a Reader tutorial. You're probably a pretty smart person and can figure it out for yourself. I just wanted to take a moment to make you aware of it in case you're not already. However... here are some of the cool things I can do: tag sets of related feeds, mark come-back-to-read-later articles with a big yellow star and share my favorite articles in an automatically maintained meta-blog thing. That last one is wicked cool because it actually has its own RSS feed so its like a feed of feeds kind of thing and as a coder I really dig the recursion of it.

Along the same lines as Reader is Bloglines. I tried it for a while before I settled on Reader. It has an equally cool set of features. There is obviously overlap as well as some interesting features that set it apart. (FWIW I settled on Reader because of some odd usability issues I had with Bloglines. YMMV)

I encourage you to try 'em both out and any others you run across. It isn't really about which one you settle on as long as you settle on something. In my opinion (of course it's mine since it's my blog) there is just no way to keep up with the torrent of information without some sort of tool that will help us organize it all.

My other favorite toy is del.icio.us. del.icio.us does for bookmarks what Reader does for feeds. It lets me aggregate all of my bookmarks into one web-accessible location and organize them by any relevant set of tags I want. What's more, my friends with a del.icio.us account can drop things onto my pile and vice-versa. They even have these cool browser buttons that let me tag a link without visiting the page. So, now, instead of maintaining an html page of my bookmarks or trying to sync six to twelve browser instance's bookmarks I just install those cool buttons everywhere I go and link 'em online.

Clearly these are not the only tools available to help manage the information overload. Even if they were the only tools for their niche we're still faced with all of our other input sources. Rather than simply sigh and succumb to the avalanche take a moment to find things to help get things under control. There are a lot of smart folks out there with the same problem and some of them are giving us some powerful tools to take back control. If I wore a hat I'd definitely take it off to 'em!