Java API override
Mechanism with java Endorsed Standards
This Topic is about a very rarely used feature of java to
override some of the java API with newer version of those APIs. I java world it is called ‘Java Endorsed Standards Override Mechanism’.
I got to know about this when I got stuck in one of the
scenario where I had some java version problem.
I was using Apache CXF 3.x runtime for Webservice. By
default, it generates Jaxb 2.2 api implementation classes, which is according
to java 7 standards. But, I was using java 6.x for generating client stub from
my ‘wsdl’. While doing so I was getting some exceptions:
For Example:
Caused by: java.lang.NoSuchMethodException:
javax.xml.bind.annotation.XmlElementRef.required()
at
java.lang.Class.getDeclaredMethod(Class.java:1937)
at
com.sun.codemodel.TypedAnnotationWriter.invoke(TypedAnnotationWriter.java:112)….
As you can see above exception shows that it is unable to
find required method in javax.xml.bind.annotation.XmlElementRef
class, because there is a change in java 6 to java 7 version. Java 7 has this
required method in javax.xml.bind.annotation.XmlElementRef, but not in Java 6,
which I was using.
Now,
What to do in this scenario. After doing little research I got to know about ‘Java Endorsed Standards Override Mechanism’. This provides a means whereby later versions
of classes and interfaces that implement Endorsed Standards or Standalone
Technologies may be incorporated into the Java Platform.
All you need to do is deploy updated packages, by placing
the packages in jar and pointing those jar using system property java.endorsed.dirs.
Eg:
-Djava.endorsed.dirs=/some/path/directory
containing packaged jars.
Or,
Put
those packaged jars in jdk’s jre/lib/endorsed
folder
One you are done with this, your updated API will come into
effect.
This overriding mechanism can be done only with Endorsed Standards
APIs and Standalone Technologies till now. More details of these can be found
at Java
7 endorsed-standard-apis.
Let us understand
this feature by an example
You can access code of this example at my git location: Test Java
Overriding Mechanism.
In this example I just wanted to show how I have written
some code which is supported by java 7 which has JAXB 2.2 API but I am trying to compile and run using
Java 6, which has JAXB 2.1 .
For this I create a JAXB Application , and for this created 3
model classes.
ContactInfo.java
public abstract class ContactInfo {
}
Address.java
import
javax.xml.bind.annotation.XmlElement;
import
javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name="address")
public class Address extends ContactInfo {
@XmlElement(required=false)
private String street;
public void setStreet(String street) {
this.street = street;
}
}
PhoneNumber.java
@XmlRootElement(name="phoneNumber")
public class PhoneNumber extends ContactInfo {
}
Customer.java
@XmlRootElement
public class Customer {
@XmlElementRefs({
@XmlElementRef(name="address",type=Address.class,required=true),
@XmlElementRef(name="phoneNumber",type=PhoneNumber.class,required=true)
})
private ContactInfo contactInfo;
public void setContactInfo(ContactInfo
contactInfo) {
this.contactInfo = contactInfo;
}
}
Here,
You can see I have used ‘required’ attribute in @XmlElementRef(name="address",type=Address.class,required=true), which is there since Java7 with JAXB 2.1. So when you try to
compile it using Java 6 it would give compilation error, because it comes with
JAXB 2.1.
cannot find symbol
symbol : method required()
location: @interface
javax.xml.bind.annotation.XmlElementRef
@XmlElementRef(name="phoneNumber",type=PhoneNumber.class,required=true)
^
Approach using ‘Java Endorsed Standards Override Mechanism’,
we can provide JAXB 2.2 jars in jdk’s
jre/lib/endorsed folder or -Djava.endorsed.dirs=/some/path/directory
containing JAXB 2.1 jars. It should compile and run successfully.
-----------------------------------------------------------------------------------------------------------------------
import java.io.File;
import java.io.IOException;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import
javax.xml.bind.SchemaOutputResolver;
import javax.xml.transform.Result;
import
javax.xml.transform.stream.StreamResult;
import javax.xml.validation.Schema;
import
javax.xml.validation.SchemaFactory;
import com.test.model.Address;
import com.test.model.Customer;
import com.test.model.PhoneNumber;
public class TestEndorsed {
public static void main(String[] args) throws Exception {
Customer
customer = new Customer();
Address
address = new Address();
address.setStreet("1 A Street");
customer.setContactInfo(address);
JAXBContext
jc = JAXBContext.newInstance(Customer.class, Address.class, PhoneNumber.class);
jc.generateSchema(new SchemaOutputResolver() {
@Override
public Result createOutput(String
namespaceUri, String suggestedFileName)
throws IOException {
suggestedFileName
= "schema.xsd";
File
file = new File(suggestedFileName);
StreamResult result = new StreamResult(file);
result.setSystemId(file.toURI().toURL().toString());
return result;
}
});
Marshaller
marshaller = jc.createMarshaller();
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
System.out.println(marshaller.getSchema());
Schema schema = sf.newSchema(new File("schema.xsd"));
marshaller.setSchema(schema);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
}
}
This
is the Main program which you can test for marshalling and generating schema
out of customer object.
You
can also use it in build scripts like ANT and Maven. For Example,
<target name="compile">
<mkdir dir="build/classes"/>
<javac srcdir="src" destdir="build/classes">
<compilerarg value="
-Djava.endorsed.dirs=C:\software\jdk1.6.0_21\jdk1.6.0_21\jre\lib\endorsed1"/>
</javac>
</target>
I Hope this
helps many who are struggling with API compatibility and its usage.
Suggestions and
Comments are most welcome.
CHEERS!!!
No comments:
Post a Comment