domingo 23 de diciembre de 2007

Sharing projects with Ant+Ivy+Jetty+Hsqldb

Sometimes (well, many times, really) you are in the need to transport a project (or application) from one machine to another. SUN proudly invented the "Write once, Run everywhere" for Java back in the day but let me share a secret with you, it's far from real. Yes, Java itself is cross-platform but there are a lots of other things involved when talking about portability, just a small list could easily include: JVM versions, JDK/JRE dependencies, different environments (OS, net, paths), third party libraries or products, etc.

If it's an already built application (binary distribution), things become easier. At least, half of the process has been done! In addition, there are some tools available (like IzPack) that help you in the labor. These tools are focused on client installations so I'll skip them today. I would prefer to talk about sharing code between developers.

Since the advent of Maven repositories Java has had a decent way to manage dependencies. Unfortunately, Maven has not come without problems and some people (me included) have opted to skip it entirely (if at all possible, of course). The good news is Ant can do everything Maven does (well, I'm not really sure if Ant can create IDE projects for you..not that Maven shines there anyway!). In our case, Ivy is the sanctioned Ant extension to deal with Maven repositories. One of the nicest features it packs is the automatic installation step (if desired), so if the target machine has Ant already installed Ivy can be used without problems. Let's look at the code:

<project xmlns:ivy="antlib:org.apache.ivy.ant" ...>
   ...
   <target name="install-ivy">
      <get src="http://...ivy.jar" dest="lib/ivy.jar" usetimestamp="true" />
      <path id="ivy.lib.path">
         fileset dir="lib" includes="*.jar"/>
      </path>
      <taskdef
         resource="org/apache/ivy/ant/antlib.xml"
         uri="antlib:org.apache.ivy.ant"
         classpathref="ivy.lib.path"/>
   </target>
   ...
</project>

The code is very simple, it just needs a namespace declaration in the project tag and adding a new target. We are now in disposition to use Maven repositories transparently. Ivy has a proprietary syntax but in the end it's a direct port of Maven's. Use an ivy.xml file with lines like:

<dependency org="log4j" name="log4j" rev="latest.integration"/>

And in the build.xml file include a new target:

<target name="resolve-dependencies">
   <ivy:retrieve />
</target>

Compile as usual and you'll notice that Ivy downloads all the needed JARs recursively. Nice! We have just scratched Ivy possibilities here but it's a good introduction to its potential (try to understand configurations next).

At this point you may have created a web application. This brings a new problem: deployment. How can you ensure that the user will have an AS installed? Or a DB? And what about a DB schema? All this kind of problems can be solved embedding a server (or various) with your code. The best of all is you don't even have to include them, Ant+Ivy will download, configure and start them alone. You have several choices but the most common by far when talking about embedded servers are Jetty and HSQLDB.

Jetty is a powerful yet lightweight servlet container with JPA capabilities (that should be enough 90% of the times). It includes ant tasks to start up and deploy a web application. In ivy.xml add

<dependency org="org.mortbay.jetty" name="jetty-util" rev="6.1.6"/>
<dependency org="org.mortbay.jetty" name="jetty-plus" rev="6.1.6"/>
<dependency org="org.mortbay.jetty" name="jetty-naming" rev="6.1.6"/>
<dependency org="org.mortbay.jetty" name="jetty-ant" rev="6.1.6"/>
<dependency org="org.mortbay.jetty" name="servlet-api-2.5" rev="6.1.6"/>
<dependency org="org.mortbay.jetty" name="jsp-api-2.1" rev="6.1.6"/>
<dependency org="org.mortbay.jetty" name="jsp-2.1" rev="6.1.6"/>
<dependency org="org.eclipse.jdt" name="core" rev="3.1.1"/>
<dependency org="org.mortbay.jetty" name="jetty" rev="6.1.6"/>

And in your build file:

<target name="run" depends="package">
   <taskdef
      classpathref="run.path.id"
      resource="tasks.properties"
      loaderref="jetty.loader" />
   <jetty tempDirectory="${temp.dir}">
      <webApp name="..." warfile="${dist}/..." contextpath="/..." />
   </jetty>
</target>

This will halt your build, boot up Jetty and deploy the specified WAR file. Most of the work is done, the final step is starting and populating the DB. When using JPA with HSQLDB the database will be automatically loaded on startup (just include the JAR in the path). JPA also includes an option to drop and create the schema. That's without doubt the easiest way to bootstrap the DB. An example with hibernate:

<persistence-unit name="..." transaction-type="RESOURCE_LOCAL">
   <provider>org.hibernate.ejb.HibernatePersistence</provider>
   <class>...</class>
   <exclude-unlisted-classes>true</exclude-unlisted-classes>
   <properties>
      <property name="hibernate.connection.username" value="sa"/>
      <property name="hibernate.connection.password" value=""/>
      <property name="hibernate.connection.driver_class"
         value="org.hsqldb.jdbcDriver"/>
      <property name="hibernate.connection.url"
         value="jdbc:hsqldb:mem:test;shutdown=true"/>
      <property name="hibernate.connection.shutdown" value="true"/>
      <property name="hibernate.dialect"
         value="org.hibernate.dialect.HSQLDialect"/>
      <property name="hibernate.hbm2ddl.auto" value="create-drop"/>
   </properties>
</persistence-unit>

Finally, you may need to populate it with data. I usually use a BootstrapListener (a ServletContextListener). You can instantiate objects and persist them using JPA or include a SQL file and load it directly, it depends on the amount of data really.

By now, and if you've done everything correctly, you have a professional build and execution platform in no more than 100 lines! As an additional security measure you may pack Ant if you aren't sure that the client will have it installed. Oh! By the way, as a much as I dislike Maven...the same results are achievable using it (sigh!).

1 comentarios:

Bob dijo...

So do you really like Ant+Ivy? Maven2 does much more than those two of course but I have been watching Ivy mature.