Friday, November 20, 2009

M2Eclipse and the dreaded ClassNotFound

M2Eclipse and split output directories
Recently I had the experience of some pain using m2eclipse, maven and eclipse. I thought i would share this with you and hopefully save you the same pain.

If you use maven2 and eclipse, m2e is very handy tool.
  1. It resolves dependencies and gives you a classpath as defined by the pom.
  2. You can edit the pom live and autocomplete things like dependency names and plugin configurations
  3. It will link snapshoted projects together for refactoring
  4. It will automatically link up source jars so you can browse the source of dependencies

I think those benefits outweight many of the issues that other people seem to baulk at.

Eclipse is my IDE

To me it seems natural that Eclipse is my IDE I use it to develop quickly with refactoring support and all the other goodness. I use maven to manage my dependencies and releases. I keep them separate so always had separate output paths for eclipse and maven. I never run maven from inside eclipse.

If you ask why, consider I want to do a release:prepare which does a clean, test then a tag. Now if eclipse is open and using the same output paths it will rebuild the classes as soon as I do the clean. The whole point of using maven for the release is that the classpath is very well defined for main compile. Eclipse merges test and compile scope and add its own jars in if you're not careful. So that means the class that are compiled might not be using the well defined classpath and I've wasted my time.

So everything in my world was good until I started a new project and was forced to install a new machine. Naturally I got the latest version of the M2Eclipse plugin and everything went to custard.


ClassNotFound

So I carried on with my standard approach of defining different output folders for eclipse. Maven would build to target/classes and eclipse to target-eclipse/classes. However without rhyme nor reason junit or my application runners would not find classes that eclipse had built.

Richard mentioned the note on the m2eclipse website saying that as of 0.9.4 the classpath was tweaked for maven in eclipse.

So I'm running Junit in eclipse so why should I care? Well I went trawling in the code base and comparing revisions to see what was going on and this is what I found.

  1. It was actually the third 0.9.4 release that made the change. I was previously using the first 0.9.4 release and had no problems.
  2. M2Eclipse intercepts the application, junit and testng launchers and scopes them properly. That means application run in runtime scope only. Tests run in test scope.
  3. Did you click?... that means that the output folders in the classapth for applications, unit and testng are the maven ones NOT the eclipse ones.
  4. M2Eclipse stores the output directoris in the workspace metadata separately from how eclipse defines them. It IS possible for this to be stale and that is incredibly confusing. This is when everything looks right everywhere but regardless your classes are still not found.
On pondering for a while I can see the reasons

  1. This was too allow proper scoping of tests and applications to stop the eclipse scope bleed.
  2. And because of this pearler... some maven plugins hardcode there paths and won't run in eclipse. I would have thought fixing the plugins would be a better idea but in any case...
It was a bit mean to just change the default rather than allowing a new option to configure it so if you had issues with a plugin you could work around it till the plugin was fixed. As stated above I think its pretty crazy to share the output paths cause you never know what you are actually getting.

Not to the solution...

How to set things up properly
So you should follow the instructions on the m2eclipse website and set up a profile for maven in your parent pom. Or check out my example. The following assume you are using my example which differs only in the profile name.

Because you are configuring how maven will run in eclipse as well as eclipse the easier approach is to use the maven-eclipse-plugin with the m2clipse goal
mvn eclipse:m2eclipse
You will need to make sure however that
  1. You enable the profile when you do that... mvn -Prunning-in-eclipse eclipse:m2eclipse
  2. You run eclipse:clean do remove the old config file mvn -Prunning-in-eclipse eclipse:clean eclipse:m2eclipse
  3. You don't have the project open in eclipse otherwise the workspace will end up stale and m2eclipse will see the old path. You might even need to clean your workspace if things go pear shaped
  4. Configure the maven eclipse plugin to enable the profile by default otherwise when you regenerate it will be turned off again
The easiest way to see this in action is to check out

svn co http://gholam.googlecode.com/svn/trunk/net.gholam.example/m2eclipse

and look at the README, you will find instruction for using the maven-eclipse-plugin in the different ways to see it generate correctly.

Don't panic
Ultimately using maven2 and eclipse you will get unexpected thing happening. M2Eclipse is only 0.9.8 so still not a release. Hopefully I have provided a little insight into the things that might go wrong when spliting output directories in eclipse.

If you done everything and you still have issues try starting eclipse with -clean to refresh your workspace and then opening and closing the relevant projects and see if that helps.

Credits
Thanks to Richard Vowles for encouraging me to start a blog, and for announcing it on IllegalArgument.com.

No comments:

Post a Comment