Friday, January 30, 2015

Java API override Mechanism with java Endorsed Standards

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