Weaving our way back to Java 1.4

There’s a really nice little open-source project on Sourceforge called RetroWeaver, which allows you to write Java 5 code and convert the compiled classes to Java 1.4. This is really useful if you want (for example) to write some objects which can be used in both 1.4 and 1.5 – you can serialize them out in 1.5 and read them back in at the 1.4 end or vice-versa.

A simple solution… with problems

One way the package can be called using the exec-maven-plugin by inserting a block like this in the <plugins> section of your Maven pom.xml file like this:

...
         <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <executions>
               <execution>
                  <goals>
                     <goal>java</goal>
                  </goals>
               </execution>
            </executions>
            <configuration>
               <includeProjectDependencies>false</includeProjectDependencies>
               <includePluginDependencies>true</includePluginDependencies>
               <mainClass>net.sourceforge.retroweaver.Weaver</mainClass>
               <arguments>
                  <argument>-jar</argument>
                  <argument>target/project-${project.version}.jar</argument>
                  <argument>target/project-${project.version}-jdk14.jar</argument>
               </arguments>
            </configuration>
            <dependencies>
               <dependency>
                  <groupId>net.sourceforge.retroweaver</groupId>
                  <artifactId>retroweaver</artifactId>
                  <version>2.0.7</version>
               </dependency>
               <dependency>
                  <groupId>net.sourceforge.retroweaver</groupId>
                  <artifactId>retroweaver-rt</artifactId>
                  <version>2.0.7</version>
               </dependency>
            </dependencies>
         </plugin>
...

Doing it this way is all fine and dandy, but left us with a couple of problems:

  1. What if you want to deploy the weaved (woven?) artifact as well?
  2. What if you’re in a multi-module project and you don’t want to RetroWeave all the modules? Where do you put the exec-maven-plugin?

Solving the issues

To solve this, I decided to write a new Maven plugin called, imaginatively, retroweave which would do all of the above for us.

Now you can add the following to <pluginManagement><plugins> section of the parent module pom.xml file, and this forms the default for all child projects:

...
    <plugin>
       <groupId>ca.intelliware.commons</groupId>
       <artifactId>retroweave</artifactId>
       <version>1.0-SNAPSHOT</version>
       <configuration>
          <skipModule>true</skipModule>
       </configuration>
    </plugin>
...

This will tell the retroweaver plugin to skip this module completely if the ‘retroweave:weave‘ goal is called.

Now, in the projects you actually want to weave, you add this block:

...
    <plugin>
       <groupId>ca.intelliware.commons</groupId>
       <artifactId>retroweave</artifactId>
       <version>1.0-SNAPSHOT</version>
       <configuration>
          <skipModule>false</skipModule>
          <artifacts>
             <artifact>${project.artifactId}/target/[target jar].jar</artifact>
          </artifacts>
       </configuration>
    </plugin>
...

This will turn on the weaver for this module, convert the target jar and deploy it to your Maven reporitory as target jar-j4.jar. Neat, eh?

  • Remember, in the pom.xml you can use Maven variables to make your life easier like this: module-${project.version} as the target jar, which will keep the version number up to date for you.

pom.xml options for retroweave

Parameter Type Default value Description
skipModule boolean false Choose whether or not to simply skip this module. Any other configuration options will be ignored.
ignoreMissingTargets boolean false If this is set to true, targets listed in the <artifact> tags which are not present when a weave attempt is made will not cause an error, but they will be logged, even if verbose is off.
targetVersion String 1.4 The target version the weave is aimed at. This must be one of 1.4, 1.3 or 1.2
verbose boolean false Turn on messages and logging.
artifacts list The list of artifacts to be woven. These are defined as <artifact> tags inside the <artfacts> holder.
verifyTarget boolean false Once the weaving is done, verify the target classes against the target JDK runtime
verifierRuntimeRoot String /usr/java/verifier This is the root directory where the various JDK runtimes are kept. See the note below for further details.

verifierRuntimeRoot

The RetroWeave verifier needs access to the rt.jar from the appropriate JDK in order to successfully verify the woven classes. The plugin is configured to look at code>
/version/rt.jar
for this, so under the verifierRuntimeRootdirectory, you need a structure like this:

verifierRuntimeRoot --+-- 1.4 -- rt.jar
                      |
                      +-- 1.3 -- rt.jar
                      |
                      +-- 1.2 -- rt.jar

If the verifier cannot find the rt.jar for the JDK version it has targeted, then the verifier will not run. Of course if you only intend to ever weave to 1.4, then don’t bother with the other JDK runtimes.

Once it has found rt.jar, the verifier adds the woven target file to the verifier’s classpath, and then follows it with the entire dependency list. This allows it to completely check all the references within the woven target.

Using the woven artifact

Once the weave is complete, the artifact is installed or deployed (or both, depending upon the Maven build target you executed) into a repository and is available for use.

To use the regular artifact from the build, you would use a <dependency> block like this in the pom.xml:

...
   <dependency>
      <groupId>com.foo</groupId>
      <artifactId>bar</artifactId>
      <version>1.0-SNAPSHOT</version>
   </dependency>
...

In order to access the newly minted, super-duper woven version, just change the dependency to look like this:

...
   <dependency>
      <groupId>com.foo</groupId>
      <artifactId>bar</artifactId>
      <version>1.0-SNAPSHOT</version>
      <classifier>1.4</classifier>
   </dependency>
...

All you have to add is the <classifier> tag within the dependency, matching the target version string you set in the retroweave definition. This will pick the woven version of the class.

  • NOTE: Sources are not woven, so if you do a -DdownloadSources=true, you will see the original source code, with all the JDK 1.5 or 1.6 goodness.

Possible gotchas

  1. Remember that Maven executes goals in sequence. Make sure you have created the artifact you want to weave before you call ‘retroweave:weave
It's only fair to share...
Share on FacebookGoogle+Tweet about this on TwitterShare on LinkedIn

Leave a Reply