Showing posts with label maven. Show all posts
Showing posts with label maven. Show all posts

1.25.2010

Top 10 reasons why Maven sucks...not!

I'm having a hard time understanding all the Maven bashing that takes place these days. Maven has been a real joy for me. It could be the simple fact that I can move from colleague to colleague's project without wondering what ant tasks are available or where the files are. It could be the fact I don't have to configure IDE's anymore, not even for downloading sources. It could be some of my favorite plug-ins. Maybe it's the 250,739 artifacts that are available in three lines of copy paste declaration. There's a lot to love.

I'll admit, it didn't make sense at first. It felt a little too large and I was leery of the potentially restrictive guidelines.  I was also very nervous to commit to the predefined life cycles and directory layout. Within a short period of time I learned a minimal pom provided more function than a minimal ant script and the conventions are actually what enable you to easily add more to the build. After an objective comparison to previous projects I've worked on I determined that while less flexible, Maven's life cycle is both adequate and consistent. Over a few years of use, adequate and consistent has proven to be far more valuable than unique or powerful. This is the build, not the software we're building.  There's plenty of business logic to make our systems complex.  The statement that alternative build tools are more powerful is also debatable.

Let me be clear, I'm not recommending we all use Maven or stop working on alternative tools. I am, however, asking that people give Maven a break. A lot of Maven criticism borders on ludicrous. We're all open source advocates (right?). If you dislike it, you know what to do. If you have a specific need, write a mojo (and share it, or don't). If you're going to speak out, offer constructive criticism. In the very least, try not to make your anti Maven rant based on a string of fallacies. I'm trying not to pick on any specific alternatives or individual, but let's walk though this one item by item:

10. maven corrupts – software, people.
Maven exposes circular dependencies and actually uses the checksums it generates. The software aspect isn't true. Maybe I'm a little corrupted, but I'd consider it more like being spoiled. There's also the expression that power corrupts and with a side note about absolute power. The availability for corruption seems to resonate more strongly with the thought of an entire programming language at your disposal. It's all up to how the power is used.
9. maven uses an archetype(appfuse) and expects that all your projects look like the apache projects – even those that are not webservers.
I'm not really sure what this means. I think Maven expects that your projects look like Maven projects. That's what the convention means. When I looked at appfuse projects, they looked like Maven projects to me but maybe it's a chicken and egg thing, which much like the riddle itself, doesn't have much incentive to solve.
8. maven has only four build stages – compile, test, install, package – if you have a requirement to automate performance testing you are out of luck
There are quite a bit of hooks in the build lifecycle. It was added later, but I believe you want integration test (with pre and post integration hooks). I use a fully automated functional test suite that deploys and fires up selenium. I heard Terracotta was using it to fire up two instances of Tomcat to test fail over scenarios. These aren't performance tests, but I'm sure it's achievable. 
7. maven requires you to have a standard directory structure that is 5 subdirectories deep – regardless of the fact that your project only has 10 java files.
src/main/java . Let's see that's 1,2,3. I think it's a good convention and so do several other alternatives.
6. tasks that are not implemented in maven have to be implemented in ant and need to be integrated with maven – a big nightmare.
The only situation where I've been forced to use Ant was to sign an applet jar to put into a war-file. That unfortunate situation was due to a defect we experienced jar:sign goal. After 10 minutes of trying to get it to work, we found a defect in a jira somewhere. To work around the issue we tossed in the Ant task from the original build script. Since then the maven-jarsigner-plugin has evolved and I'm pretty sure we could remove the ant-run task if we were into fixing what ain't broke.
With that said, it's stupidly simple to use ant-run. You don't even have to go download Ant, nor does the n'th machine that your build will run on have to download it.  Nightmare? I've spent more time setting ANT_HOME and configuring PATH variables.
5. maven encourages you to integrate via binaries – thus making continuous integration difficult
I've used Hudson and Bamboo extensively. Both have excellent facilities specific to Maven. I don't even understand what the alternative to binary integration is. I wish the complaint had a specific scenario for me give a valid counter point. 
4. every time maven builds it connects to the internet (to verify/update dependencies) – building your project without the internet is extremely hard.
Not true.  Maven grabs it once then it never needs the internet except once a day for snapshot, which is for your convenience and can also be disabled. Don't forget offline mode. And you really should take 5 minutes to setup Nexus for a little durability in the event of an internet outage.
Follow up question, how easy is doing anything without the internet? Specifically, how easy is executing code you don't have on your local machine without the internet?
3. maven manages the projects and subprojects implicitly (since there is convention and then there are overrides) – it makes debugging your build impossible.
You can always see the effecive-pom if you need to. It's not very hard to see exactly what overriding is going on.
2. maven manages the project dependencies in its own repository – called the .m2 repository – which is not part of your application folder – thus making it impossible to track and package development environments.
This repository is the local repo. It is not part of your application folder because you probably have more than one project that requires commons-logging.jar.  You can track which files make up your development environment pretty easily. You can also copy them to your development environment or package them for several different environments. Pretty flexible facilities in place for dependency management
1. maven always downloads the whole internet – to keep its dependencies up to date – even though you don’t want to.
Whole internet? At this point I'm trying hard not to reply with sarcasm. I'm not trying to attack the author, just disprove the points. And to address the point, 2.0.9 and up has predefined plug-in versions. They do not update on their own. Anything else it updates because you didn't tell it not to by specifying a version for your plug-ins.

This is the type of FUD you can easily find by doing a Google search for "Maven sucks". Here's why I believe it doesn't suck.

Maven's declarative model allows you to prepare structured data, the primary application of XML, to a system that knows how to do what you want. Very goal oriented. The primary motivation of Ant was to have platform independent scripts for Java which was platform independent. That's why at the time, Ant was hot sauce on fried chicken! The problem for me with scripting language based build systems is that most remain task oriented. Staying disconnected from the tasks required to complete a goal is a great gain in overall usability. Maven stays focused on the latter and offers the flexibility to tweak the tasks when necessary.

In addition to the lack of branch logic and variable declarations, the main gripe on XML is that it  is too verbose. But so is the documentation for an ENTIRE LANGUAGE!!! DSL subsets make it easier but there's still a learning curve, especially if someone gets clever. Learning Maven definitely does not happen immediately, but what you are learning is domain knowledge for the task at hand, build management. Every other build tool I've given the cursory glance to required I learn about its build process in addition to a full language or DSL. XML, for all of its lousiness, is ubiquitous and self describing. Lack of cleverness can be a good thing.

That might all be a moot point since polyglot maven is right around the corner. I'll probably stick to XML because I don't write XML, Eclipse does (so do many text editors, with syntax and error checking to boot). Still I am very interested in the project and using it as my primary excuse to get some Clojure in my life. I do have fearful thoughts regarding what tricks someone will put into their builds using these alternatives, but if it means more people are happy about Maven I'm all for it.


After all, why shouldn't Maven cater to those who crave the additional power. Let's not forget that it was created to help you, not make your life hell. There's an age old expression I learned from Gary Coleman at a young age regarding different strokes and world domination. Some people drive stick and others prefer automatic. I prefer automatic because it allows me to do other things like spam text messages instead of shifting. A manual transmission might just encourage me to speed off the road or get a traffic violation. There's a method to the madness of preventing someone from acting foolish. Think of that traffic law as a metaphor to prevent a rake script gone crazy. I personally feel that when Maven isn't making something easy, it's because it probably shouldn't be done in the first place, not in the build at least. For all other times, there's the gmaven plug-in, but I really really try not to make my colleagues go through the unfortunate pain of learning a new language just to execute my tests or create a custom jar file.  

Is the power of a Turing complete language really necessary to compile, execute tests, package and/or deploy your code?

So to wrap it up, I don't understand the hatred towards Maven. Some people act like it stole their prom date. I'm happy for alternatives like buildr and gradle and realize no matter how great any project is it will eventually be replaced.  I don't discourage people from trying or developing new tools, even though I would love to see those efforts put into Maven instead. Different strokes!  Opinions aside, it is and will forever be a tool that redefined build management and whatever tool replaces can only improve upon the baseline it has established.

But the inaccurate, almost childish Maven ranting has to come to an end! It's downright ungrateful and disrespectful to the people who have worked very hard to provide a free and open source option.  Newer alternatives greatly benefit from the groundwork laid by Maven in the first place including the repository or directory layout conventions. So in the least pay homage

10.12.2009

Cygwin + Interpreters(Jline) + Carriage return = solved!

I love cygwin. Not only is having every a bash shell and unix utility on windows awesome(grep, less, tail, awk, cut, pipe, sed, du, df, ls, ln, curl, sort, head, vi, X-Server, etc...) but using rxvt or xterm as a replacement to windows cmd window is awesome.

However when I use some command line interpreters such as a groovy shell, maven cli, spring roo, and others, my carriage return doesn't work. When I hit enter, the cursor goes to the next line but the command is not executed. This has been bothering me for months and yesterday I finally found the answer.

The core of the problem is with JLine, a command line API that many Java based interpreters use. Jline queries the environment to see what OS you are running and uses that for the expected end of line input. Because the OS is windows but the terminal is xterm, cygwin (rxvt at least) will not send the command Jline is expecting to signal end of line. Therefore, you can't actually execute anything in the interpreter.

So after searching for a solution over and over I finally stumbled upon this issue. Thank god for JIRA! It ultimately led me to this Jline defect.

So in order to save you a lot of trouble, all you have to do is add the following to your jvm args:
-Djline.terminal=jline.UnixTerminal


If you are doing this for maven CLI, then you need to add the -D argument to your MAVEN_OPTS because Maven will execute the process for you.

I didn't need to use the stty commands as recommended but I only fixed my maven-cli plugin (which is awesome btw). Maybe someone could explain what the stty commands are for and when or why you'd need them.

So hopefully this will save some cygwin users some time and frustration.

9.13.2009

Running tests from a Maven test-jar in your build

So one of the less frequently used features of Maven is that it allows you to package and deploy the test classes and resources to the repository. While the documentation suggests it's to reuse the tests, you can only use these as a standard dependency, not to re-execute them.

Well I was recently challenged with the task of re-executing the tests. I won't discuss the motivation behind the request as I'm not certain it's worthwhile. For now let's just assume you've been asked to do this too. I'll save you some time and share some of the things I discovered when trying to accomplish this.

So obviously we'll need to have the test-jar in the repository (see link above). We'll then need to pull it down and have surefire to execute it. The problem is that surefire does not have a "test jarfile" goal. So we'll need to leverage the unpack goal of the dependency mojo. Here is the xml for that:

<build>
<plugins>
...
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-dependency-plugin</artifactid>
<executions>
<execution>
<id>unpack</id>
<phase>process-test-classes</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifactitems>
<artifactitem>
<groupid>com.hirn</groupid>
<artifactid>projecta</artifactid>
<version>1.0-SNAPSHOT</version>
<type>test-jar</type>
<outputdirectory>${project.build.directory}/additionaltests/projecta/</outputdirectory>
</artifactitem>
</artifactitems>
</configuration>
</execution>
</executions>
</plugin>
...



I'm assuming some basic Maven knowledge here but please comment if you have more questions about how this is configured. There are a couple personal selections worth discussing here.

The first is the outputDirectory within the artifactItems section. It may be tempting to unzip into src/test/java so that surefire will just execute the tests as a normal part of the cycle. This isn't good though because you could potentially clobber a test that already exists there. You can't create a separate directory within /src/test/java either because the package names wouldn't be correct anymore. I choose to use ${project.build.directory}/additionalTests/ (aka target) so that things get cleaned up naturally. It's also important to unzip each artifactItem into a separate folder to avoid possible namespace collisions.

The second is when to execute the goal. Process test resources seemed like a natural selection here, even though it's slightly against purist Maven lingo as the "resources" directory non class files. Another option could be to use the pre-integration-test. Just as long as it's before the phase we configure surefire actually execute the tests.

Since we extracted the executable .class files to a separate directory, we need to tell surefire when and what to execute. Here's one example:


<build>
<plugins>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<executions>
<execution>
<id>project-a-tests</id>
<phase>test</phase>
<goals>
<goal>test</goal>
</goals>
<configuration>
<testClassesDirectory>${project.build.directory}/additionaltests/projecta</testClassesDirectory>
<reportsDirectory>${project.build.directory}/surefire-reports/projecta</reportsDirectory>
</configuration>
</execution>
</executions>
</plugin>



Pretty straightforward. You have to make sure the phase is declared after the phase where the dependency is unpacked and point it to corresponding directory. The other piece to consider is that you don't overwrite the surefire report produced from running the rest of the tests. I couldn't find a way to aggregate the the report but wither or not you want to is personal preference. Would you even want your CI or reporting tool to track the success of executing these tests? Probably not, but you certainly don't want to affect the output of the tests within the projects.

You can download an example of this to see it in action. Just `mvn install` projecta and then do the same for projectb. You'll see the test run from projecta all over again.

Why I had to do this seemed to be pretty special case. My situation was driven by a peculiar scenario. There was a project A with dependencies A->B->C where A has a defect due to code that lives in C. Rather than create a new B, we just want to touch C and modify A such that A->B,A-C'.

One way to look at it is that there's no code change in B so why build it? The alternative is to say why not? Not only is rebuilding cheap (or should be), version numbers are free and it communicates to other projects using B that they need to upgrade to B'.

So if you have a good reason for executing tests from a deployed test-jar, I'd like to hear it. The whole time I was doing this it felt unnatural. I've never had to do this in over two years of extensive Maven use. The thing I love about Maven is that if it doesn't work automatically, you probably shouldn't be doing it. If this were truly useful, there would be a surefire:execute-jar goal.