Recently, a small internal team did some research work on setting up a Maven infrastructure. The findings of that team were extremely positive, and as Jessamyn and I started working on a new prototype project, we elected to use Maven from the get-go. What we’re interested in recording, here, are some of the real-life stories of woe (and joy!) that we’ve encountered as we’ve wrapped our heads around what Maven means to us.
More Good Than Bad
We’d be lying if we said that our first foray into Maven was completely painless. In fact, most of this blog entry is devoted to recording some of the places where our knees got scraped as we trotted down the path of Maven. But, in general, I’m happy that we used it, and I can see the benefits.
In particular, I finally feel like there’s an easy way of putting a bunch of stuff into common projects/.jar files that are shared between projects. And the fact that Maven provides good support for versioning of those common elements removes a lot of the fear factor that Things Will Change!!! It’s also a lot easier to depend on other components sitting in the repository.
It was also really easy to set up a new project that is similar to an existing project. You could copy a pom.xml, tweak it to suit our personal tastes, and Maven could flesh out the details.
We also didn’t spend a week setting up a build server: we used the existing Continuum infrastructure, and had our project building really quickly.
Jessamyn likes the fact that Maven insists on running the tests (and passing!) before you, say, try to deploy to server. Some people who are not Jessamyn are a bit more neutral on that topic.
Jessamyn brought up a good point: that because she doesn’t have a lot of experience going through the pain of setting up the initial project, she didn’t have as much to compare against. Me, I have tons of pain to compare against.
Initial set-up was pretty simple. There’s good starter info on the Maven FAQ. We were up and running pretty darn quickly.
The Long and Winding Path to Subversion
Two early annoyances that we ran into involved Subversion. We were happy to use Subversion for source control (’cause, heck, that’s what the cool kids are doing these days), but we had a hard time interacting with Subversive’s “Share Project” dialog box. Subversive seemed to have Very Clear Ideas about how we should name things in the repository, and they weren’t the same Very Clear Ideas that our Subversion guy had originally proposed. Eventually, we decided to see the world the same way that Subversive sees the world and we think we’ll be better off for it.
The other annoyance (probably related to the first) is that applications that are composed of multiple Eclipse projects are kinda tricky to set up correctly. The first time we tried building something in Continuum using the “old Subversion project naming structure”, Continuum couldn’t figure out how to check the assets out of the repository. We wished (oh, how we wished) that this was ground that had been covered internally, but our hopes were dashed upon the rocks.
Why, Oh Why, Does SVN Hate the Target Directory?!?
Maven has strong opinions about how projects should be laid out. One of Maven’s strong opinions is that everything should be compiled and packaged in a “target” directory. Now, Eclipse is usually pretty smart about assuming that you don’t want your build folder checked in to the repository, but in a Maven project, Eclipse thinks your build folder is “target/classes”. And, um. Eclipse is easily fooled into thinking it’d be a Good Thing to check the “target” folder (but not the “target/classes” folder) into source control.
Don’t. Just don’t. It’s scarily easy to do, but once you accidentally check your target folder into the repository, soon you find your jar files, and published web site and all kinds of other stuff desperately trying to get into the source control system. Help stop the madness before it starts.
In practice, every time we created a new project, we’d go through the “Share Project” process, but we’d have to be careful to cancel just before the commit stage. Then we’d hop in to the package explorer so that we could add the “target” directory to svn:ignore.
The Quest for Group/Artifact Ids
It was surprisingly late in the process when we discovered the browseable Maven Repository. If you search for a dependency name, say “hibernate”, it will generate a list of links to associated packages. The page for each package contains various bits of useful information, including all released versions and a formatted POM dependency clause.
Before we knew about this tool, we’d do a whole lot of Google searching, looking for group ids and component ids to add to our POM file.
So, we got underway pretty quickly. We built our first few projects. Added a bunch of obvious dependencies, and started running the “mvn install” process. Life seemed to be going well, and we were happy.
And then, we banged our heads against an annoying Maven error message. Maven kept telling us it couldn’t resolve a dependency on commons-logging:commons-logging:jar:RELEASE. Huh?
It was an extremely painful session of looking through all of our dependencies, and all of their dependencies trying to figure out what specifically was triggering that problem.
The root problem was that the official published Maven POM files for Spring 1.2.7 say they depend on commons-logging but don’t specify a version. When we have a dependency on something that depends on, say, commons-logging 1.0.4, and we have another dependency on something that depends on Spring 1.2.7, it seemed that Maven would try to decide if commons-logging 1.0.4 was compatible with commons-logging (version unspecified). And it’d get confused.
The problem was even more opaque because our project had a dependency on Spring 1.2.9. The only reason 1.2.7 came in to the picture was because something else we needed pointed at 1.2.7. Life would be ever so much better if there was an easier way of navigating and/or visualizing the dependencies. As it was, we spent a lot of time tracking this particular error down.
All Them Exceptional Cases
Some important .jars don’t seem to have officially published versions in any of the standard repositories. For example, Sun’s Java Transaction API (JTA) libraries don’t have Mavenized versions.
The problem is so well-known that Maven has a whole page devoted to the topic. (But, as they say, everyone complains about the weather and nobody does anything about it).
Less frequent were libraries (such as the ROME-Fetcher code that’s part of Sun’s RSS API, ROME) where the only official sources are so old, they’re virtually useless.
These exceptions are clearly not the majority, but when they do happen, and we have to do “manual installs” of jars, we aren’t really in our happy place.
Damned MyEclipse Deployment
Okay, I like JBoss. I use it a lot, and I think it’s solid and reliable and it works, and I’d take JBoss over most other Application Servers (*cough*WebSphere*cough*) any day. But JBoss did a very, very bad thing when they bundled Log4J the way they did.
Usually, the thing you need to remember is that any .war or .ear you deploy to JBoss must not, in the name of all that is holy, include the log4j.jar. Otherwise, you are in for a long night of trying to understand crazy ClassCastExceptions.
But herein lies the problem. Since Maven is now taking responsibility for managing our jars, it often wants to include Log4J as one of its dependencies (especially if something you depend on needs it).
And if you use MyEclipse to deploy your Eclipse project to your JBoss server, then there’s a good chance you’ll end up deploying something that includes a log4j.jar. Wacky hijinks ensue.
an exclusion capability in Maven POMs which proved helpful (albeit ugly). I want to revisit the way we’ve set this up, but at the moment, the only way we could get Maven and MyEclipse to work together required us to run a Maven install prior to every new deploy of the application. One of the consequences is that if I’m tweaking a CSS file or some other resource, that resource won’t hot deploy. I need to re-run the install/deploy process.
Whose REPO Is It Anyway?
Our practice at Intelliware seems to be to share machines, and have one common place to put our workspace. Different projects have slightly different behaviour, but it’s common to have things like, say, all the Eclipse projects sitting in:
Here’s the thing, though. Maven really likes the idea that your Repository is sitting in your user.home directory, like so:
C:\Documents and Settings\myuserid\.m2\repository
What’s more, we have an Eclipse variable pointing at this repository. If one person logs out and another logs in, wacky hijinks ensue.
What Directory Does Continuum Run My Tests In?
Getting our project building in Continuum was Really Amazingly Simple. But there was one gotcha.
Often, when I write unit tests that need to access something from a file in my project, I’ll do something like this:
File file = new File("../myprojectname/someDir/myfile.txt");
Classic example: I want to test my web.xml, and it isn’t on my classpath, so I can’t just read it in as a resource.
The reason I use “../myprojectname” prior to the file name, is that when I have multiple Eclipse projects, and I want to combine all the tests from those multiple Eclipse projects together, the suite will run in the context of whichever project contains the AllTests runner.
Continuum, however, isn’t so fond of that, because when it checks out projects, it doesn’t create a folder called “myprojectname”. Instead, I get something like this:
And, hey, surprise!, my test fails. I don’t have a full solution to this just yet, and I’m fairly confident that I could write a tricky little utility to spot the different environments and react accordingly. Tests would run one way in Eclipse and another in Continuum. But I wish I didn’t have to.
We like Maven. We really do. But we got some bruises along the way.