JAX-RPC supports two major ways of developing Web services:
“bottom-up”, which allows to generate WSDL from a Java interface and “top-down”,
which generates Java classes from WSDL. Both of these approaches suffer from
one major drawback.
WSDL/Schema/mapping files or Java classes are fully re-created every time there is a need
to change Web service interface and so it becomes a developer’s responsibility to
merge existing WSDL files or Java code with the generated files. The need to manually
change WSDL/Schema stems from the fact that the generated files are usually very “basic”,
they do not take advantage of advanced schema capabilities, they
don’t use WSDL/Schema modularization and so on. Generated Java classes are even more problematic.
You really don’t want to have simple “value” classes without any behavior with fully exposed
properties. And adding behavior requires manual intervention.
In reality, there is a way to evolve two sets of artifacts (WSLD
and Java classes) independently without any generation by manually updating
Java-WSDL
mapping file. The format of this file, however, is the antithesis of the
“convention over configuration” idea. The format is extremely verbose; each and every
field has to be mapped explicitly and so manual modification of this file poses a
real challenge for any more or less complex data structure.
The latest JEE Web service specification, JAX-WS, fixes this problem by
heavily leveraging annotations.
For data mapping, annotation support
is provided by JAXB specification. JAX-WS and JAXB finally free developers from
having to rely on the brittle code generation approach.
In fact, JAXB still supports code generation and so developers can use
it for generating Java or WSDL files. The generated artifacts could provide a good starting point for a
Web service interface. After that, however, it is pretty easy to keep both sets of
artifacts in synch by updating annotations and/or WSDL/Schema files.
How does it work in practice? Let’s say we have this very simple schema:
<element name="person">
<complexContent>
<sequence>
<element name="firstName" type="xsd:string" />
<element name="lastName" type="xsd:string"/>
</sequence>
</complexContent>
</element>
The following class will match the schema:
// By default JAXB uses getters/setters to drive marshaling/unmarshaling
// Using fields is easier, since they can be defined more compactly
// in one place and also moved around to manipulate order.
// Beware though - getters/setters are ignored in this case
@XmlAccessorType(XmlAccessType.FIELD)
// We need to provide the namespace, otherwise it defaults to
// the package name
@XmlRootElement(namespace="http://myarch.com/personservice")
public class Person {
// note that JAXB marshals instance variables in the order they
// declared
protected String firstName;
protected String lastName;
...
Note that JAXB provide several
additional attributes and annotations which I’m not using here – I’m trying to rely as much as
I can on “convention over configuration”. Element names, for example, match my variable names,
so there is no need to provide annotations for individual fields (however, in this case,
you can’t use qualified locals
in your schema since JAXB won’t prefix nested elements.)
Many JAXB annotations, such as “XmlType” are only used for generating schema from Java classes.
Same is also true for most attributes of XmlElement.
Oftentimes developers specify “required” and “nillable” attributes of XmlElement annotation
thinking that JAXB will automatically enforce these constraints, e.g. :
@XmlElement( required = true, nillable=false)
private String lastName;
However, these parameters are not used at
run-time. JAXB 2.0 actually relies on the
schema validation framework and so
these parameters can be omitted assuming that the schema contains the constraints.
In other words, we only have to use those annotations that help
us to correctly marshal/unmarshal our objects.
The only other step that we need to take is to create ObjectFactory
class in same
package with the mapped class. In this class we need to define a factory method for the root
element of our content tree (you don’t need to worry about nested datatypes even
if they map to other classes). From what I understand, JAXB does not actually invoke any
factory methods, however, it does check for their presence in ObjectFactory
.
The factory method looks simple enough:
public Person createPerson() {
return new Person();
}
Now we can make changes to our Java classes and the corresponding WSDL/Schema files without
ever having to resort to code generation. In my mind, this is a preferred way.
We can see from Object-Relation mapping that “pure” “top-down” and
“bottom-up” don’t really exist. How often do we generate DDL from Java classes or vice versa?
Different data representations supported by different technologies have to be able to evolve
independently without having to play by each other rules. JAXB helps us to achieve it pretty easily.
Hi ,
This is very good approch by which we do not have to maintain java classes in our code , define xsd and use jaxb to generate classes.
I was trying with this approch and could create and deploy web service on jboss4.2.1GA with this approch
The process i followed is
1. define xsd
2. use xjc task on the xsd to generate java classes
3. in web service use this generated java class as paramter to web service method
4. deploy the web service in jboss
once the web service gets deployed i can see the wsdl by using the url http://localhost:28080/jbossws/services
Now i am trying to genarate wsdl from the java class using wsprovide ant task from jboss
but it fails to generate wsld giving ClassCastException