Maven Invoker Plugin 1.8, more than just another minor release

november 27, 2012

The latest version of the Maven Invoker Plugin has just been released on it deservers some extra attention.

The Invoker Plugin is used to run a set of Maven projects. The plugin can determine whether each project execution is successful, and optionally can verify the output generated from a given project execution.
This plugin is in particular handy to perform integration tests for other Maven plugins. The Invoker Plugin can be employed to run a set of test projects that have been designed to assert certain features of the plugin under test.

Set debug logging on project-level

With the previous versions of the invoker plugin it was possible to change the logging level to debug, but that would apply on every invoked project. Now you activate debug logging per project by adding the following line to your invoker.properties:
invoker.debug=true

Debug invoked projects

Up until now the mvn executable was always called when invoking a project. This made it very hard to debug the invoked projects. Now it is possible to change the name of the executable, so you can use mvnDebug to debug.
Either add your preferred Maven command to the arguments like -Dinvoker.mavenExecutable=mvnDebug or add it to the configuration of the maven-invoker-plugin:

<configuration>
  <mavenExecutable>mvnDebug</mavenExecutable>
</configuration>

Selectors for Maven and JRE work for forked instances as well

For both Maven and Java you can specify if a project should be invoked based on their versions.
The invoker.properties could look like this:

# A comma separated list of JRE versions on which this build job should be run.
invoker.java.version = 1.4+, !1.4.1, 1.7-

# A comma separated list of Maven versions on which this build should be run.
invoker.maven.version = 2.0.10+, !2.1.0, !2.2.0

These values were always calculated based on the running Maven and Java versions, and not on the forked versions (i.e. when you specify a different mavenHome and javaHome for the invoker plugin).
But now the plugin can also handle these forks, so projects will only be invoked if their target Maven version and Java version match.
This feature required the change of the Maven prerequisite, so you will need at least Maven 2.2.1 to run the invoker plugin. But invoked projects can use a lower version of Maven by just pointing the mavenHome to the preferred version.

Exclude tests

In some circumstances there can be tests which fail on your system. If somebody else is trying to fix this, then it shouldn’t block your development. In such cases you want to disable these known failing tests from commandline. With this release you can do the following:
-Dinvoker.test=!testX
This way it is possible to exclude specific tests.

Report skipped invoked projects

Both the logging and reporting have been improved by mentioning why an invoked project was skipped. The report now follows the layout of the surefire-reports much better.

Project information

To use this plugin, add the following to the build section of your pom.xml

<plugin>
  <group>org.apache.maven.plugins</group>
  <artifactId>maven-invoker-plugin</artifactId>
  <version>1.8</version>
</plugin>

Maven Invoker Plugin Site: http://maven.apache.org/plugins/maven-invoker-plugin
Release notes: http://jira.codehaus.org/secure/ReleaseNote.jspa?projectId=11441&styleName=Html&version=18730

New Mojo Logo Contest

januari 20, 2012

MOJO-1800
Carlos Villaronga has designed 6 proposals for a new logo.
Some of the logo’s were posted with different sets of colors, but for this contest I’ve decided to offer only one color set.
Let’s first choose the preferred logo and depending on the responses see if we need to vote for colors as well.

I won’t make this poll complexer than necessary: vote and after a week I’ll publish the results.

How to create a jar containing reusable/abstract testclasses with Maven

september 5, 2010

Now you’ve done it: you have discovered that creating unit tests is not for that single moment anymore, since testing has become a part of the Maven default build lifecycle. Now you can use these tests over and over again so that it can conform the code still acts as it should. It’s worth to create a lot of unit tests, more than ever.
Although a unit test should test only one single unit (as the name says), there are situations in which you want to use an abstract testcase, for instance to test the contract of an interface. As long as this abstract class is only used in one project, it’s just a straight forward story by adding it to the src/test/java-folder. But once it covers multiple projects, you want to share these abstract testclasses, which mean you have to create a new project for it.
Now the most important thing you need to understand: These sources must be put under src/main/java-folder and its depending jars should have the default of compile scope. As weird as this may sound: you’ll give the junit (or other favorite testing framework) dependency the compile scope too, since the main sources depend on it. And these (test)classes can be tested too of course, by creating specific junittests for those files.

I noticed I can generate a test-jar (jar with the testclasses) with the jar-plugin. It contains exactly what I need. Why not use this dependency by adding the test-classifier?

First you have te be aware that all jars, with or without classifier use the same pom.xml. Just check any dependency with source and/or javadoc jars in a repository, you’ll see only one pom.xml. Next thing you have to know is that if you assign a dependency to your project, only the transitive dependencies required at runtime will be resolved, no matter the scope you give it inside your project. Just think about it: are you interested in all (if any) the dependencies which junit uses to test its classes? Combining these facts makes it not possible to build the right test-classpath. If you really want to, you could add these dependencies by hand, but then you’ll lose the transitive dependency resolution of Maven.

Next Projectlead QDox

februari 16, 2010

What a year! It all started one year ago: I made my entrance into the world of open source communities by joining the QDox team. I won’t repeat myself, just take a look at my first blogentry.
It’s just one single year, but what a great year it was with six continuously improving deliverables of QDox.
And it didn’t stop! By the end of december 2009 Joe Walnes contacted me, asking if I would like to take over QDox. A few e-mails later I was willing to lead QDox. An official vote was called an the team fully supported the request.

Just let me quote Joe, I think it’ll say it all:

from thread New QDox team lead – Robert Scholte

Hello all

I’d like to announce the new lead for QDox, Robert Scholte.

Anyone who’s been following the project for the past year would have seen
that Robert has made a tremendous number of contributions to all areas of
QDox, including bug fixes, build, docs/website, releasing, user support and
admin. If you follow the project further back, you’ll see that Robert has
actually done more on the project in the past year than was done for the
combined 6 years before that…. impressive.

I originally wrote QDox back in 2002, as a stop gap until annotations were
added to the Java language. It was quickly picked up tools such as XDoclet.
I always thought (and hoped) that once annotations made it into the
language, it would be redundant. Yet it lived on. Unlike the JDK Annotation
Processing Tool (APT), it could process source code rather than byte code in
a simpler and faster manner. And it provided access to information that the
JDK didn’t make available (e.g. parameter names). And it was useful where
annotations still did not exist, like in JavaME. So, even today, it still
lives on and is used in tools such as Maven and Eclipse. But I cannot invest
the time into it that I once could.

Given Robert’s commitment to the project, I can think of no better person to
lead the project. He’s done an astounding job. Robert has kindly accepted
this role and I’m delighted to hand it over to him.

I’d also like to use this opportunity to thank Paul Hammant, Mauro Talevi
and Dennis Lundberg for their ongoing efforts on the project.

cheers
-Joe (QDox founder, and former lead)

Develop & Deploy SNAPSHOTS, Release Deliverables

januari 15, 2010

De meeste maven-gebruikers kennen ondertussen de ‘default-lifecycle’-phases wel. Termen als ‘compile’, ‘test’ en ‘install’ kunnen de meeste maven-gebruikers wel dromen. Maar daarmee eindigt de defaul-lifecycle niet, er is namelijk ook nog een deploy-phase. Daar waar install ervoor zorgt dat het package-resultaat in je locale repository terecht komt, zal deploy deze naar een externe repository uploaden (bijvoorbeeld een nexus-applicatie in het netwerk). Vanaf dat moment kunnen andere ontwikkelaars er ook gebruik van maken zonder zelf steeds de broncode te updaten en te installeren op eigen machines.
Tot op een gegeven moment alles klaar is voor een officiele release. Hoe zorg je ervoor dat de codebase en/of deliverable van een specifieke release ook later nog te achterhalen is? Hoe voorkom je menselijke fouten? Hoe verzeker je, dat elke releasemechanisme gelijk verloopt ongeacht de uitvoerende ontwikkelaar? O ja, en wie op de eerste vraag ‘inpakken+branden’ heeft geantwoord: klinkt als de enige en de zwakste schakel. Lees dat het ook anders kan!

Snapshots vs. Deliverables

Zoals de term ‘snapshot’ al suggereert is het een package op een zeker moment voordat de definitieve versie opgeleverd wordt. De myproject-1.0.0-SNAPSHOT van gisteren hoeft dus niet gelijk te zijn aan de myproject-1.0.0-SNAPSHOT van morgen. Dit in tegenstelling tot myproject-1.0.0, welke ook na 2012 nog steeds exact hetzelfde zal zijn. En dit is ook hoe Maven ermee omgaat. Bij het aanroepen van bijv. compile wordt eerst gekeken of er SNAPSHOT’s tussen de dependecies van het project staan. Zo ja, dan wordt gecontroleerd of hier nieuwe versies van beschikbaar zijn. Bij de niet-Snapshots gaat Maven ervan uit, dat controle niet nodig is.
Vandaar ook, dat je altijd ontwikkelt aan een Snapshot-versie! Anders zou je in theorie namelijk voor elke checkin een versieverhoging moeten doorvoeren in je pom. Want uiteraard blijven deze regels ook gelden binnen je subversion- of cvssysteem. Alles blijft draaien om reproduceerbaarheid, ongeacht welke revision je uitcheckt. Elke non-Snapshot versie mag maar 1 keer voorkomen (eigenlijk 2, namelijk onder trunk en tags). Om dit proces te ondersteunen kunnen we gebruik maken van de release-plugin.

Maven-release-plugin

De twee belangrijkste onderdelen van deze release-plugin zijn de prepare– en perform-goals. De prepare-goal zorgt voornamelijk voor de communicatie met het source control management systeem (cvs, svn, etc.), kortweg SCM. De perform-goal doet het daadwerkelijk deployen. Het is dus ook mogelijk om je project in ieder geval in je SCM definitief te maken, te labelen en te prepareren voor de volgende development fase, zonder het project ook te deployen!

release:prepare

Zoals al gezegd gaan we hiermee het project inchecken, labelen en prepareren voor verdere ontwikkeling.
Het is hierbij niet noodzakelijk om van een specifieke buildmachine gebruik te maken, aangezien deze plugin ook nog een aantal controles uit zal voeren.
Laten we beginnen met de pom.xml, welke een klein beetje uitgebreid moet worden.
We nemen een scm-sectie op, met daarin minimaal de developmentConnection welke wijst naar de root van het project. Zie ook pom#SCM.
Verder hebben we scm-client nodig op het systeempad (svn.exe of cvs.exe onder Windows bijv.)
Om te controleren of we stappen vergeten zijn, kunnen we droog gaan oefenen:
mvn release:prepare -DdryRun=true
Er zullen 3 vragen gesteld worden, elk met een te verwachten antwoord zodat je slechts hoeft te ‘enteren’. Vervolgens worden de volgende stappen uitgevoerd:

  • Controleer, dat er geen oningecheckte bestanden zijn
  • Controleer, dat er geen SNAPSHOT afhankelijkheden zijn
  • Wijzig de versie in de pom van x-SNAPSHOT naar een nieuw versienummer (resultaat van eerste vraag)
  • Wijzig de SCM informatie in de pom door het toevoegen van het doel van de ‘tag’.
  • Voer de tests uit om te bevestigen dat ze ook nog werken met de gewijzigde pom
  • checkin de gewijzigde pom
  • Label de code in de SCM met de versiewaarde (resultaat van tweede vraag)
  • Wijzig de versie in de pom naar een volgende SNAPSHOT (resultaat van derde vraag)
  • checkin de gewijzigde pom

Als de dryRun goed is doorlopen, kun je een echte release:prepare uitvoeren. Je zult dan ook meteen merken of alle instellingen kloppen. Daarnaast moeten ook de gebruikersnaam en wachtwoord bekend zijn. In sommige gevallen zijn deze al op je systeem bekend door eerdere checkins, maar je kunt deze gegevens ook meegeven met -Dusername= -Dpassword=. Er zijn nog een aantal andere opties, maar die zijn vaak OS en SCM-client specifiek.
Tot slot over de prepare-goal. Er wordt een bestand aangemaakt, welke bij houdt hoe ver je de prepare-phase doorlopen hebt, mocht het onverhoopt toch niet in één keer lukken. Klopt er iets in de pom.xml niet, zet de gegevens weer terug (x-SNAPSHOT versie) check dit in, waarna je eerst eventueel eerst een release:clean uitvoert zodat je weer vanaf het begin kunt starten.

release:perform

Deze goal voert standaard zowel een deploy uit van de deliverable als een deploy-site van de bijbehorende versie (de online documentatie).
Uiteraard kun je zelf aangeven, of je inderdaad dit default gedrag wilt hebben of dat je bijvoorbeeld liever alleen een deploy doet (zie perform-mojo.html#goals).
Voor een release:perform wordt trouwens eerst de code uitgecheckt op basis van de tag. Op basis van deze bestanden wordt de deployment uitgevoerd.

Conclusie

Maak dankbaar gebruik van deze plugin, gebaseerd op de best practices omtrent release- en deploymanagement.
De titel zegt het eigenlijk al:

  • Ontwikkel altijd aan SNAPSHOT-versies
  • Voor het verplaatsen van een SNAPSHOT naar een externe repository, voer mvn deploy uit
  • Voor het veilig stellen van een versie in SCM, gebruik mvn release:prepare.
  • Voor het opleveren van een deliverable via een externe repository, voer mvn release:perform uit.

JUnit test Struts applications with mock objects and Maven

november 28, 2009

Let me start by giving credits to Walter Jia who wrote the article Unit test Struts applications with mock objects and AOP. This was a great article which pushed me in the right direction. I wanted to use Maven instead of Ant, which meant I had to configure a pom.xml. There are a few details which you need to know to migrate to Maven. I wanted to stay close to the original example, so I didn’t upgrade EasyMock and JUnit yet.

Project layout

Below is the project structure based on the standard directory layout described by Maven. The aspect-folder is defined by the aspectj-maven-plugin. You could put the .aj-file inside the src/test/java, but this way you keep the files in their own folder.
The org.acme.StrutsActionPreExecuteListener is a helperfile, but it’s java so I’ve put it in the src/test/java folder.

  • StrutsTest2
    • src/main/java
      • com.foo.bar.ActionService
      • com.foo.bar.SimpleAction
    • src/main/webapp
      • WEB-INF
        • struts-config.xml
        • web.xml
    • src/test/aspect
      • org/acme/StrutsActionPreExecuteNotifier.aj
    • src/test/java
      • com.foo.bar.SimpleActionTest
      • org.acme.StrutsActionPreExecuteListener
    • pom.xml

The Pom.xml

I hope you’ll all understand the dependency-section, so I’ll just focus on the build-section.
First of all, the MockStrutsTestcase expects a WEB-INF/web.xml and a WEB-INF/struts-config.xml on the classpath. I want to make use of the files which will be deployed with the war as well. So I’ve defined these two files as testResources. If you want to use a specific test-version, you have to make these files inside src/test/resources/WEB-INF and they’ll be picked by Maven by default.

<testResources>
    <testResource>
        <directory>src/main/webapp</directory>
        <includes>
            <include>WEB-INF/*.xml</include>
        </includes>
    </testResource>
</testResources>

I already mentioned the aspectj-maven-plugin, which we need to include as well. It’s pretty straight-forward. Include the plugin and add an execution-section. Defining the plugin goals (in our case only test-compile) is enough. This goal is bound to the process-test-sources phase of Maven, so there’s no need to specify a phase inside the exection-section.

<plugin>
 <groupId>org.codehaus.mojo</groupId>
 <artifactId>aspectj-maven-plugin</artifactId>
 <version>1.2</version>
 <executions>
  <execution>
   <goals>
    <goal>test-compile</goal>
   </goals>
  </execution>
 </executions>
</plugin>

It looks like we’re done. But if we run the tests, both tests will fail because of a NullPointerException. The service isn’t injected, which means the preActionExecuteOccured method isn’t called.
So here’s the pitfall. It’s not (only) the test class which has to be compiled with AspectJ, especially the Action class needs to be compiled like this as well, but only for testing! So we have to tell Maven to use these files too. This is done by configuring the aspectj-maven-plugin.
Add to the execution-section the following lines:

<configuration>
  <weaveMainSourceFolder>true</weaveMainSourceFolder>
</configuration>

This way the Action is added to the test classes and will be compiled by the aspectj-maven-plugin. Note that this Action is not the one that will be packaged, since Maven uses seperate folders for main classes and test classes.
You might want to finetune this, because now all (re)source files on the main path will be added to the test classpath, so some fileoverwriting might occur.

The complete build-section will look like this:

<build>
  <testResources>
    <testResource>
      <directory>src/main/webapp</directory>
      <includes>
        <include>WEB-INF/*.xml</include>
      </includes>
    </testResource>
  </testResources>
 <plugins>
  <plugin>
   <groupId>org.codehaus.mojo</groupId>
   <artifactId>aspectj-maven-plugin</artifactId>
   <version>1.2</version>
   <executions>
    <execution>
     <goals>
      <goal>test-compile</goal>  <!– use this goal to weave all your test classes –>
     </goals>
     <configuration>
       <weaveMainSourceFolder>true</weaveMainSourceFolder>
     </configuration>
    </execution>
   </executions>
  </plugin>
 </plugins>
</build>

The Strutstest2.zip can be downloaded from Box.net

Maven advantages (company point of view)

september 29, 2009

So you discovered Maven, but you haven’t got a clue how to convince your company to introduce it?
Maven might sound like another nerdy thing a developer just wants to try. Your boss wants to see numbers, proven solutions, costs, benefits, etc. It’s true, Maven does have a (big) learning curve, but that’s more because Maven offers so much functionality. It’s much more than a compiler wrapper, but that is where it starts.
So here are some magic words which should get everyone’s interest: more stable releases and less to no regression, real modular-based development.

Use Maven beside your current builder

While everybody is working on a certain project, you’re free to introduce maven no matter the structure of the project. The only thing you have to do is add one file: the pom.xml. The other developers might notice this file but it won’t bother their project.
In other words: the risk is zero!
This is of course very important. While one person is focused on the Maven project configuration, all others can continue their development work as they’re used to. If the original builder was based on an ANT-script, Maven should be able to cover it all. If you use some special ANT-functionalities, there’s an ant-plugin available for maven to run these scripts.
By configuring your projects pomfiles it’s quite easy to proof that Maven can do at least what the original builder could do. Once the project is fully mavenized migration is very simple, because it’s actually already done. The most important thing to do now is to decide that from now on Maven is the leading builder-mechanism. If it doesn’t build with Maven anymore, somebody broke the code. Also: dependencies will be managed by Maven.

Introduce project team members in seconds

In the ol’ days helping another project team would take a lot of time. If you’re using Eclipse, you have to create a new workspace, checkout the projects (including all jars like that commons-lang for the gazillions time), setting all the paths, adding the libraries to the project, setting variables, setting jdk-version, etc, etc. and than: fingers crossed.
With Maven all this kind of information is stored inside the pom. You can run it from the command line, or with a little help from the right IDE-plugin (you should try m2eclipse for example). Just run Maven and see you’re project getting build. Also: because of its predefined structure every developer should be able to read the pom-file and understand every part of the java-project. You should try that with an Ant-file…

Don’t release with failing tests

In the ol’ days if you wanted to check some functionality of a class, you would write junit-tests, run them until they’re all green and …. Well, there’s actually no ‘and’, because most tests are used only once. That’s a shame, because this piece of code will help you stabilize your code. With Maven it’s not possible to package without all tests succeeding. (Don’t tell anyone about the ‘skip’-options.) This way it should be very hard to get regression. Also, fixing bugs will follow a standard process: reproduce the bug, write the test (test will fail) and fix the bug (test will succeed). Tests won’t be written anymore for single usage, it may become the most reused code.

Check out smaller projects

In the ol’ days every java-project was stored completely with their depending jars. If you had multiple projects depending on shared jars, you would add a special directory with these jars. From the java-project you had to point to this folder. Often these jars were checked in into some Source Control Management (SCM) System like cvs or subversion, sometimes you had to link to a shared network-folder. One way or another: you always had to modify your project manually to get it buildable.
Personally I believe jars shouldn’t be stored together with the sources of the project. It should only contain the project-sources, not any third party stuff like jars.
With Maven it’s possible to keep your SCM as clean as possible. When using jars as dependencies, Maven will refer to a (local) repository. There is no reason to download a jar by hand and add it to your project in some way. Also: upgrading jars has become much easier. Just change the version of the dependency in the pom and Maven will do the rest. Another interesting thing: Maven can help you support real modular jars. Quite often managers talk about modules, but in the end they’re just part of that one huge project because it’s hard to keep control over separated (versioned) jars. With Maven such modular jar is just another dependency. Upgrading is nothing more than changing the version inside the pom. But to break up one huge project into a core and several modules requires some migration-steps. Every project requires its own strategy, so that can be stuff for another blog.

Say goodbye to the jar-hell and welcome updates

In the ol’ days adding or updating a jar could very well result in new errors because of it’s own missing dependencies. Hopefully there’s some documentation available which lists it’s dependencies, but most of the time you would have to search every corner of the internet collecting those missing jars. So most of the time projects stick to a certain version of a jar until the end of days.
With Maven your project contains a pom with all its depending jar. But such jar (if mavenized) also contains a pom with al its dependencies. This way Maven is able to collect all direct and indirect dependecies of your project.

Experience the advantage of a Company Repository

One important product, related to Maven is a repository manager. A maven repository itself is nothing more than a structured directory tree. But with some help of a repository manager you will have extra power to control your projects. (Right now the best choice would probably be nexus, since it has been co-written by some maven-core developers)

Reuse company code

Almost every developer has its own brilliant piece of code he wants to use on all his projects. In the ol’ days it was quite hard to use the same code in several projects without copy-pasting. And we all know what that means: bug fixes won’t be fixed on all copies, new features and improvements won’t be shared.
A repository manager has a space to collect this type of company-code. By deploying/uploading such jars to the repository manager all other projects can make use of the same codebase.

Increase download speed, decrease load

One of the functions of the repository manager is proxying the maven central repository (and if required other maven repositories). If all developers point their Maven to the repository manager, all jars have to be downloaded only once (from the other side of the world). From that moment the jar is available between your company walls and developers can get it from there. Now you will also have some sort of backup for your jars, so once used jars won’t disappear anymore.

Experience the advantage of a Continuous Integration Server

A continuous integration server is a neutral building server. A project is only valid if it can be built on this machine. So the top-10 developer-quote “But it works on my machine…” cannot be longer accepted. It would be encouraged to release your projects from this machine. Don’t claim time from a developer’s machine and also don’t trust those machines (with their special settings and utilities).
Continuous Integration servers have the ability to run builds every now and than. This can be based on time (every night) or on check in. This way developers will be very soon triggered if they broke a build. They can immediately fix their features instead of discovering the bugs during tests (or production).

All this together (and there are probably more convincing reasons) could help you introducing Maven. If your audience it technical enough you could also mention that you don’t have to write or maintaine build-scripts.

So I started to work my mojo

juli 28, 2009

Hier het vervolg over de setup-maven-plugin, na mijn eerste en tweede blog, die ik hier al aan had besteed.

Ik had mezelf voorgenomen om deze zomer de plugin in zoverre af te ronden, zodat er eerst op geschoten kon worden. Eerst maar eens de reacties afwachten voordat ik begin met optimaliseren. Zo was ik ondertussen zelf al tegen een wat ongelukkig gekozen stukje implementatie aangelopen, dus die heb ik moeten herschrijven inclusief de bijbehorende tests. Ik was in staat om twee stappen tot één te combineren, waardoor het allemaal net wat efficienter werd.
Het leek me daarna tijd om de plugin aan te bieden, dus hiervoor heb ik een Jira-issue aangemaakt. Vervolgens heb ik eens gekeken hoeveel plugins er in de wachtrij staan. Hmmm, dat waren er 40. Ik moest dus wat extra’s verzinnen om deze plugin onder de aandacht te brengen. Aan collega’s met een jira-account heb ik gevraagd om te stemmen op de plugin. Daarnaast heb ik nog gesproken met Benjamin Bentmann, een developer waarvoor ik een aantal QDox-issues heb weten op te lossen. Hij is betrokken bij vrij veel codehaus- en apache projecten. Hij suggereerde om toe te treden tot het mojo-team. Dat zou de kans van slagen voor deze plugin tevens vergroten. Dus ik heb mezelf aangeboden en na een stemronde (ja, zo werkt dat nu eenmaal) mocht ik toetreden tot het team.

Maar wat betekent dit? Maven is een plugin-gedreven tool. Daarvoor heeft Maven een tweetal plekken waar hij standaard zoekt als de naam van een plugin niet volledig is uitgeschreven (soort van shortcuts). Allereerst wordt gezocht in de apache-plugin-repository. Als de plugin daar niet bestaat, wordt er gezocht in de codehaus-plugin-repository. Hierin staan de plugins, die geschreven en onderhouden worden door het mojo-development team. En daarvan ben ik dus sinds kort officieel lid.

O ja, nog even voor wie geinteresseerd is in de volledige quote van Austin powers

setup-maven-plugin stage two

juni 15, 2009

Afgelopen maart heb ik al een eerste aanzet gedaan voor een blogreeks over een nieuwe maven-plugin die ik aan het schrijven ben. Nu na ongeveer drie maanden zijn er tal van nieuwe features toegevoegd ten opzichte van het vorige artikel.

Zo is het ondertussen mogelijk om met zogenoemde dynamische xml-bestanden te werken. Dit houdt in dat de templates voor een specifiek xml-bestand expressies kan bevatten, welke via systeemproperties, externe propery-bestanden of meegegeven parameters gevuld kunnen worden. Dit klinkt misschien erg complex, dus zal ik het met een voorbeeld uitleggen.

<settings>
 <proxies>
 <proxy>
  <id>mycompany</id>
   <active>true</active>
   <protocol>http</protocol>
   <host>proxy.somewhere.com</host>
   <port>8080</port>
   <username>${proxy.mycompany.username}</username>
   <password>${proxy.mycompany.password}</password>
   <nonProxyHosts>www.google.com|*.somewhere.com</nonProxyHosts>
  </proxy>
 </proxies>
</settings>

In de bovenstaande template van een settings.xml zie je 2 expressies staan: ${proxy.mycompany.username} en ${proxy.mycompany.password}. Deze moeten natuurlijk per persoon verschillen. Er zijn 2 manieren om deze expressies van een waarde te voorzien:

Als extra parameter bij de plugin-call: Dit betekent, dat je de plugin als volgt aanroept
mvn setup-maven-plugin:global-settings -Dproxy.mycompany.username=Duke -Dproxy.mycompany.password=s3cr3t

Een tweede manier is om een bestand aan te maken (default naam is in dit geval settings.properties)

proxy.mycompany.username=Duke
proxy.mycompany.password=s3cr3t

Daarnaast is het ook mogelijk om systeemparameters in je template op te nemen, welke ook vervangen kunnen worden door de bijbehorende waarde.

Een andere feature die ik nog niet genoemd had, maar welke wel keurig in dit rijtje past zijn het ondersteunen van verschillende scm-settings. Het blijkt dat voor een aantal SourceControlManagement systemen aanvullende settings-bestanden ingesteld kunnen worden. Ook daar ben ik mee bezig geweest. Helaas is een deel hiervan nogal slordig opgezet, waardoor het niet mogelijk is om er goede tests voor te schrijven. Maar aangezien ze in de basis hetzelfde zijn, heb ik ze al wel geprepareerd. De volgende scm-systemen worden ondersteund: clearcase, cvs, git, svn, vss.

Tot slot is er een goal toegevoegd, waarmee je wachtwoorden geencrypt in je settings-security.xml kunt opslaan. Ook hiervoor zijn de bovenstaande filter-mechanismes beschikbaar.

Doel is momenteel om de code op te schonen en een aantal gelijke stukken code onder te brengen in eigen classes. Vervolgens zal de code aangepast worden naar de codehaus standaarden en is het deze zomer vast rijp genoeg om aan te bieden.

NPE’s, nullables, not-nulls and commons

april 28, 2009

Iedereen maakt ze vast nog wel wekelijks mee: NullPointerExceptions. In sommige gevallen ligt dat aan de slechte routine(s) van de ontwikkelaar, maar vaak aan de methodes doordat je niet direct kunt zien welke argumenten null mogen zijn en of het mogelijk null teruggeeft.
Door je code wat handiger op te zetten, kun je al een deel van deze NPE’s voorkomen. Als we beginnen met equals vergelijkingen, zorg er dan voor dat je begint met de constante en niet met de variabele. Vergelijk bijvoorbeeld "excellent".equals(comparisson); met comparisson.equals("bad");
Van comparisson is niet te voorspellen wat de waarde is, het zou eventueel null kunnen zijn. Door te beginnen met de constante kan er geen NPE ontstaan. Deze volgorde zie je ook terug bij de assert-methodes van JUnit. Daarin geef je eerst de expected waarde op (de constante), gevolgd door de uitkomst van een methode of variabele.
Het kan natuurlijk voorkomen dat je twee variabelen wilt vergelijken. In dat geval biedt commons-lang uitkomst. Commons-lang bevat een aantal utility-classes, welke voornamelijk null-safe zijn. Voor het vergelijken van objecten kun je gebruik maken van org.apache.commons.lang.ObjectUtils.equals(java.lang.Object object1, java.lang.Object object2). Deze methode vergelijkt de objecten en geeft 'true' terug als beide objecten gelijk of beide null zijn. Voor uitgebreidere String-vergelijkingen kun je gebruik maken van de StringUtils.

Hiermee is al de meeste pijn verzacht. Maar toch zou het handig zijn als je aan de methode kon zien (of bepalen) dat de mee te geven argumenten niet null mogen zijn en of het eventueel null kan retourneren. Kortgeleden viel mijn oog op een artikel aan van Stephen Colebourne. Daarin wordt het nullable-probleem netjes uiteengezet inclusief een oplossing of voorstel hiervoor. Annotations als @nullable en @notnull zien er elegant uit, maar kunnen nog steeds niet voorkomen, dat je tijdens het compileren gewaarschuwd dan wel afgestrafd wordt. Persoonlijk voel ik niet zoveel voor een ‘#’ als marker, maar meer voor een nieuw reserved word als bijvoorbeeld notnull. Het zou in ieder geval een enorme aanwinst zijn als de javacompiler met deze functionaliteit wordt uitgebreid.

Tot op zekere hoogte zou de java-compiler geinstrueerd kunnen worden om null-waarden af te vangen, maar daarmee kan slechts een deel van de NPE’s voorkomen worden. Developers zullen altijd in hun code rekening moeten houden met potentiele NPE’s, want bij bijna elke punt maak je weer kans om in de prijzen te vallen. Door vergelijkingen slimmer te definieren en door gebruik te maken van null-safe utilities zoals grotendeels in commons-lang en commons-collections is de kans op deze veel voorkomende error aanzienlijk geslonken. Zo zie je maar: met wat kleine aanpassingen kun je je code al snel stabieler krijgen.