As is commonly known, Oracle have finally taken over Sun and yesterday they announced what they are intending to do with the various services provided by Sun.
Unfortunately one of those announcements was that they were withdrawing the Kenai project from public use at some point in the near future. This is a pity because the Kenai project was becoming one of the more useful forges out there.
During today there’s been a lot of forum activity on the kenai site from a lot of project owners about what to do in migrating away, including myself.
So, after a good year of being hosted on kenai I’m now faced with moving to yet another forge – but where to go and what’s going to be involved?
Well the steps needed are (in order of importance):
- Move the source repositories
- Move the maven repository
- Move the jira issues
- Change all links to the new location(s).
As an initial measure I’ve mirrored the mercurial repositories on bitbucket so at least they are mirrored elsewhere. One of mercurial’s features is being distributed so that was relatively painless.
The next two are tricky so I’m still researching them, but they all depend on what forge to use. I’ve used sourceforge in the distant past so I don’t want to go back to them, and although bitbucket does have wiki and a tracker – it’s not jira.
Anyhow here’s the new repo information for the bitbucket mirrors:
- https://username@bitbucket.org/petermount/reteptools/
- https://username@bitbucket.org/petermount/retepmicrokernel/
- https://username@bitbucket.org/petermount/retepxmpp/
- https://username@bitbucket.org/petermount/retepjavadoc/
As soon as I’ll have more information I’ll make another post here, on twitter and on identi.ca
When generating Java sources from an XML Schema with JAXB, the type of field used to represent an attribute is determined by it’s type in the schema.
For example, a snippet from jabber-client.xsd:
<xs:element name="message"> <xs:complextype> <xs:attribute name="from" type="xs:string" use="optional"> <xs:attribute name="to" type="xs:string" use="optional"> </xs:complextype> </xs:sequence> </xs:element>
By default, JAXB would create the object (Message in this case) with two String properties. To change this to another object (say JID) we would add a simple binding changing these to use JID and define two static methods who’s job it is to convert between a String and a JID.
So the bindings would be something like:
<bindings schemalocation="jabber-client.xsd"> <bindings node="/xsd:schema/xsd:element[@name='message']/xsd:complexType"> <bindings node="xsd:attribute[@name='from']"> <property> <baseType> <javaType name="uk.org.retep.xmpp.JID" parseMethod="uk.org.retep.xmpp.jaxb.adaptor.XMPPDatatypeConverter.parseJID" printMethod="uk.org.retep.xmpp.jaxb.adaptor.XMPPDatatypeConverter.printJID" /> </baseType> </property> </bindings> </bindings> </bindings>
Now for mose usecases this is fine and works pretty well. The problem is when you have a large number of custom bindings.
What JAXB does for each binding is that it generates an anonymous Adapter class. In that adaper class are two methods which call the two methods declared in the binding – in the case of binding to a Double you would get the following:
public class Adapter1
extends XmlAdapter<String, Double>
{
public Double unmarshal(String value) {
return (javax.xml.bind.DatatypeConverter.parseDouble(value));
}
public String marshal(Double value) {
if (value == null) {
return null;
}
return (javax.xml.bind.DatatypeConverter.printDouble(value));
}
}
The problem here is that JAXB generates one of these for every instance, so you can end up having multiple Adapter classes present, all doing the same thing. In one instance I had a single schema contain 16 duplicates of the above code. In retepXMPP I had almost 60 spread over multiple schemas just for handling JID. This is a lot of wasted, duplicated code and if you use a large number of schemas then this is going to cause both your jar sizes and permgen use to increase for no real reason.
So it would be nice to reduce this down to a single adapter.
Fortunately there is a way when using the JAXB RI. There is an alternate javaType binding which takes the full class name of an adapter class instead – using this causes JAXB to use that single class instead of generating these duplicate classes. The main difference is replacing the parseMethod and printMethod attributes with a single adapter attribute, and qualifying the javaType with the http://java.sun.com/xml/ns/jaxb/xjc namespace.
So the bindings would be something like:
<bindings
xmlns="http://java.sun.com/xml/ns/jaxb"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:retep="http://retep.org/xml/ns/retepTools"
version="2.0">
<bindings schemalocation="jabber-client.xsd">
<bindings node="/xsd:schema/xsd:element[@name='message']/xsd:complexType">
<bindings node="xsd:attribute[@name='from']">
<property>
<baseType>
<xjc:javaType adapter="uk.org.retep.xmpp.jaxb.adaptor.JIDAdapter" name="uk.org.retep.xmpp.JID" />
</baseType>
</property>
</bindings>
</bindings>
</bindings>
</bindings>
Then just create a single adapter implementation. in retepXMPP thats some 60 classes reduced to one, if you generate any javadocs based on the generated sources there’s no more “Adapter” spam – large numbers of apparently duplicated classes in the javadocs etc.
Tip: Just make sure you keep an eye on that namespace – miss it out and XJC will spurt out some unusual error messages.
Update: I’ve disabled comments on this article simply because spammers are the only ones posting comments to it. Peter 27 Feb 2010
In this article I’ll briefly cover how to use Groovy to generate java sources as part of a maven build.
There are times when you need to generate Java source which is either repetitive, based on external data or both. Now there are tools out there for some tasks like this, but for most simple tasks running a groovy script is more than enough.
Now for the purposes of this article, I’m going to presume we need an enum which represents the cards in a Standard deck of cards, one entry per card.
First we need to create our groovy script. Now the best place to put this is in the src/main/script directory of your project. Here’s the script for this (writing groovy is not in scope for this article) which goes into the file src/main/script/CardDeckGen.groovy:
class CardDeckGen {
public generate( project, String packageName, String className ) {
// Where to write the classes
File targetDirectory = new File( project.build.directory + '/generated-sources/groovy' )
// The directory to write the source to
File packageDir = new File( targetDirectory, packageName.replace( '.', '/' ) )
// Now to create our enum
def out = []
out<<'package '+packageName+';\n'
out<<'public enum '+className+' {\n'
// We have four suits
def suit = [ 'D', 'S', 'H', 'C']
// Each suit has A, 2-9, T, J, Q & K
def rank = [ 'A', '2', '3', '4', '5', '6', '7', '8', '9', 'T', 'J', 'Q', 'K' ]
// For each suit, write an entry for each card
suit.eachWithIndex {
s, i -> def indent = i==0 ? ' ' : ',\n '
rank.each {
r -> out<< indent + s + r
indent = ','
}
}
// Mark the end of the enum list
out<<';\n'
// Finish the enum class
out<<'}\n'
// Convert the array into a string
StringBuilder sb = new StringBuilder()
out.each { sb.append(it) }
// Now write the source, ensuring the directory exists first
packageDir.mkdirs()
new File( packageDir, className + ".java" ).write( sb.toString() );
}
}
Next we need to get maven to run this script, so in pom.xml we first need to make sure we are using the correct version of the jdk – 1.5 or 1.6 would be enough:
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
Next groovy, here we add the gmaven-plugin which runs a small code snippet calling our class:
<plugin>
<groupId>org.codehaus.groovy.maven</groupId>
<artifactId>gmaven-plugin</artifactId>
<executions>
<execution>
<phase>generate-resources</phase>
<goals>
<goal>execute</goal>
</goals>
<configuration>
<scriptpath>
<element>${basedir}/src/main/script</element>
</scriptpath>
<source>
CardDeckGen cdg = new CardDeckGen()
cdg.generate( project, 'uk.org.retep.example', 'CardDeck' )
</source>
</configuration>
</execution>
</executions>
</plugin>
Now that’s enough to generate our enum, however maven doesn’t know about it so it wont compile. To do that we need to add it so the compiler picks it up:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-groovy-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source>${project.build.directory}/generated-sources/groovy</source>
</sources>
</configuration>
</execution>
</executions>
Now you can run maven.mvn clean install
If all goes well you’ll get a maven artifact containing an enum:
package uk.org.retep.example;
public enum CardDeck {
DA,D2,D3,D4,D5,D6,D7,D8,D9,DT,DJ,DQ,DK,
SA,S2,S3,S4,S5,S6,S7,S8,S9,ST,SJ,SQ,SK,
HA,H2,H3,H4,H5,H6,H7,H8,H9,HT,HJ,HQ,HK,
CA,C2,C3,C4,C5,C6,C7,C8,C9,CT,CJ,CQ,CK;
}
This was a simple example but quite a powerful one. I’ve used this technique for generating more complex versions of Card’s to classes based on data contained within RFC’s (STRINGPREP etc).
For help on writing Groovy stripts take a look at the Groovy Getting Started Guide.