Tuesday, July 25, 2006
Start with an XML schema and everything else just drops out?
JAXB Binding Stage
- In the XML schema an
eventList
consists of a sequence ofevent
elements. The default JAXB binding is to model this asList<Event> getEvent()
; for the sake of correctness, we have a directive in thebinding.xjb
file to say that the corresponding property should be calledevents
, therefore we get the correct plural formList<Event> getEvents()
. - The schema for XML schema itself, has a plethora of types related to capturing dates and / or times (check out the full list here). We went for the
dateTime
type in our schema (which basically captures a date and time down to the millisecond with timezone offset). JAXB created a new class to model this in Java calledjavax.xml.datatype.XMLGregorianCalendar
. In Java it's more typical to handle a date time as ajava.util.Date
(or ajava.sql.Date
). For us it made sense for the JAXB object to model it as ajava.util.Date
as this is a type that all JDO implementations must be able to handle and model by default. However, this meant that we had to create an adapter class that can convert from a string inxs:dateTime
format to aDate
and another to perform the reverse. (Thankfully there are utility methods to help in this regard!). Then we add another directive in thebinding.xjb
file referring to this adapter class and an instruction to model this "property" as aDate
in the corresponding JAXB class.
- It has to be said that JAXB 2.0 models XML schema enumerations beautifully now; it's as natural and as obvious a mapping was one could imagine! However, not so JDO 2.0. Certainly, for this version of the specification they could not quite agree on how to do this formally! With JPOX what you have to do is first of all download a plugin jar
jpox-java5.jar
and make it available to the runtime. Secondly you can choose to model the corresponding Javaenum
as either a JDBCVARCHAR
or as anINT
. For clarity when viewing the database tables during development we choseVARCHAR
. This has to be added as a directive in thepackage.jdo
mapping file.
Friday, July 14, 2006
Using apt with Ant.
apt
(annotation processing tool) is a tool that comes with JDKs versions 1.5 onwards. It performs the desired transformations on annotated source code. As we need it for our builds (to run over code containing JAXB annotations) I decided to search for an Ant task to do this (not really wanting to write one myself!). The latest stable version of Ant is 1.6.5
. This does not yet have an apt task. Out of interest I had a look at a checkout of the HEAD of the Ant CVS repository. This does, but the version of Ant overall is only described as 1.7-alpha
. After some googling, I uncovered from a tutorial that there is one in Sun's JWSDP, or more specifically JAX-WS. You don't need all of the JWSDP, just JAX-WS 2.0 standalone is fine. The main drawback I found is that it doesn't do dependancy checking, therefore I have to add my own "up-to-date" check in the build.xml
to compare the timestamps of source code and apt-generated class files, but otherwise, everything works OK.
Thursday, July 13, 2006
Making a datasource available to the application.
PersistenceManagerFactory
in JNDI, but JPOX doesn't include the supporting classes to do this! Anyway, for this project for greater harmonization with Java EE projects in general we've chosen to make the underlying datasource available as a widely understood JDBC javax.sql.Datasource
. Alternatively it is possible just to include the JDBC driver jar in the war itself and obtain a JDBC connection directly.Whatever underlying datasource we're using, we initialize our
PersistenceManagerFactory
via a jpox.properties
file. The key differences are that for a datasource configured without JNDI, specific properties look like this:No JNDI
...
javax.jdo.option.ConnectionDriverName=
org.apache.derby.jdbc.ClientDriver
javax.jdo.option.ConnectionURL=
jdbc:derby://localhost/db/trecx-store
...
JNDI
...
javax.jdo.option.ConnectionDriverName=
org.jpox.driver.JPOXDriver
javax.jdo.option.ConnectionURL=
jpox:java:comp/env/jdbc/TrecxStore
...
So, where as in the no-JNDI configuration we specify the JDBC user, password, driver and connection URL in
jpox.properties
, with the JNDI configuration we specify it in the file/META-INF/context.xml
which ends up in the war file. This file also contains instructions including where it should go in the default JNDI context (this is how you add a datasource to JNDI with Tomcat; other servlet containers do something similar). Although some what opaque(!) the specification of a driver of the type org.jpox.driver.JPOXDriver
and a connection URL of jpox:java:comp/env/jdbc/TrecxStore
informs the JDO helper class that there is a javax.sql.Datasource
instance available in the default JNDI context at jdbc/TrecxStore
.Tuesday, July 11, 2006
Modelling a data schema from scratch.
In our project we have had the "luxury" of being able to design our data schema from scratch. In other words, we have not been given a data schema that we have to match. However, I think it's interesting to see the forces at work and know where to focus one's energies. For instance, as this is a 'web service' we've always known that there was going to be an associated schema, in this case, XML schema (although equally we could have used DTD or Relax NG). However, we've also known that the data has to be persisted some how. Therefore, we could have concentrated on the (relational) schema of the database. However, as it is, because I was particularly keen to abstract persistence away, it made sense to concentrate on the XML schema itself. By doing this we could have even used an XML database such as eXist. By opting to use JDO with JPOX, this actually generates the relational schema for us from the persistence-capable classes that we specify (which in turn, were JAXB classes generated from an XML schema!). However, you can still work in reverse; if we'd had an existing relational schema, we could add directives in the
package.jdo
file to specify how we wanted the properties of classes to map to columns in tables. (We do have some directives in fact; we have to specify how Java enum
s get mapped to columns as the JDO JCP have not agreed on how to standardize this yet!). So, intriguing things to ponder. I guess what it boils down to is you use whatever "schema" language (relational database, XML schema, etc) that you feel most comfortable with.Monday, July 03, 2006
Use of JDO v. hyperjaxb2.
hyperjaxb2 (cons)
- one-man band effort.
- partially documented.
- "straddles" 3 technologies (and thus documentation!).
- uses older JAXB (JAXB 1.0).
JDO (pros)
- well maintained.
- well documented.
- standard API (rather than library). Therefore, leaves open the possible option of using a commerical (superior?) implementation instead.
- tools support; Eclipse plugins and Ant tasks.
In practice I was finding that when working with hyperjaxb2 I was having to have 3 pieces of documentation open at the same time; hyperjaxb2, Hibernate and JAXB 1.0! This is because it is a composite process, specifically, it consists of enhancements to the JAXB schema generator that uses Hibernate to achieve persistence. As I've posted previously, in terms of clarity there really is no comparison between JAXB 1.0 and JAXB 2.0. There are plans for hyperjaxb to use the latter (the confusingly named hyperjaxb3!), but this is even less mature than hyperjaxb2, (which itself is only version
0.4
!) and does not have any binary releases yet.What I like about JDO (we're using the reference implementation JPOX incidentally) is that XML and persistence stages are quite separate. What we do is use JAXB to generate classes from an XML schema. We then give those classes to JDO which makes a sub-set of these classes persistence-capable, as specified via a mappings file (
package.jdo
) through a process of byte-code enhancement. This persistence-capability is effectively transparent in the sense that no (public
) methods are added to these classes (unlike hyperjaxb2 which for example adds a primary key method to each persistable class). We can then use these same artefacts to drive the automatic creation of the relational database schema (from a choice of vendors, naturally!). Just like Hibernate it has it's own database agnostic query language, in this case JDOQL
. This makes it easier to switch database vendors without needing to change the application itself. The main motiviation we had for doing it this way, is that it should be easier to alter the event XML schema (which is far from cast in stone!) and then automatically generate all the downstream artefacts without too much thought.