Wednesday, August 3, 2011

How to Simply Convert SDO to DOM Element and Vice-versa

In previous post, I demonstrated how to marshall POJOs to SDO. In this post, I will show how to convert SDOs to DOM Elements without parsing them to or from String.


The key is the following methods from the commonj.sdo.helper.XSDHelper:
  • void save(XMLDocument xmlDocument, javax.xml.transform.Result outputResult, java.lang.Object options); -- wherein, we can instantiate a DOM node and wrap it with a javax.xml.transform.dom.DOMResult;
  • XMLDocument load(javax.xml.transform.Source inputSource, java.lang.String locationURI, java.lang.Object options); -- wherein we can wrap a given DOM element with javax.xml.transform.dom.DOMSource;

Below is a client I add on the sample application from previous post:
package soadev.sdojaxbsample.clients;

import commonj.sdo.DataObject;
import commonj.sdo.Type;
import commonj.sdo.helper.XMLDocument;
import javax.xml.bind.JAXBContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.dom.DOMSource;
import oracle.bpel.services.common.util.XMLUtil;
import org.eclipse.persistence.sdo.helper.jaxb.JAXBHelperContext;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import soadev.hrservice.model.Job;

public class SDOToElementClient {
    public static void main(String[] args) {
        SDOToElementClient client = new SDOToElementClient();
        try {
            client.testConvertSdoToDomElementAndViceVersa();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void testConvertSdoToDomElementAndViceVersa()throws Exception{
        System.out.println("----Start testConvertSdoToDomElementAndViceVersa----");
        JAXBContext jaxbContext = JAXBContext.newInstance(Job.class);
        JAXBHelperContext jaxbHelperContext = new JAXBHelperContext(jaxbContext);  
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        jaxbHelperContext.getXSDHelper().define(loader.getResourceAsStream("soadev/hrservice/model/JobSDO.xsd"), "soadev/hrservice/model");
        DataObject sdo = createSampleSDO(jaxbHelperContext);
        System.out.println("Created sample SDO: " + sdo);
        XMLDocument xmlDocument =
              jaxbHelperContext.getXMLHelper().createDocument(sdo, sdo.getType().getURI(), "jobSDO");
        DocumentBuilder docBuilder =
            DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document document = docBuilder.newDocument();
        jaxbHelperContext.getXMLHelper().save(xmlDocument, new DOMResult(document), null);
        Element element = document.getDocumentElement();
        System.out.println("SDO converted to DOM element: " + element);
        System.out.println(XMLUtil.toString(element));
        System.out.println(">>> Converting element to SDO...");
        XMLDocument xmlDocFromElement = 
              jaxbHelperContext.getXMLHelper().load(new DOMSource(element), null, null);
        DataObject sdoFromElement = xmlDocFromElement.getRootObject();
        System.out.println("DOM element converted to SDO: " + sdoFromElement);
        System.out.println(sdoFromElement.get("jobId"));
        System.out.println(sdoFromElement.get("jobTitle"));
        System.out.println(sdoFromElement.get("maxSalary"));
        System.out.println(sdoFromElement.get("minSalary"));
        System.out.println("----End testConvertSdoToDomElementAndViceVersa----");
    }

    private DataObject createSampleSDO(JAXBHelperContext jaxbHelperContext) {
        Type jobType = jaxbHelperContext.getType(Job.class);
        DataObject jobSDO = jaxbHelperContext.getDataFactory().create(jobType);
        jobSDO.set("jobId", "SOADEV");
        jobSDO.set("jobTitle", "SOA Developer");
        jobSDO.set("maxSalary", 20000L);
        jobSDO.set("minSalary", 18000L);
        return jobSDO;
    }
}

Run Output...
C:\Oracle\11g\jdk160_24\bin\javaw.exe -client -classpath ...
----Start testConvertSdoToDomElementAndViceVersa----
Created sample SDO: soadev.hrservice.model.JobSDOImpl@1372656
SDO converted to DOM element: oracle.xml.parser.v2.XMLElement@1fa6d18
<ns0:jobSDO xmlns:ns0="/soadev.hrservice.model/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <jobId>SOADEV</jobId>
   <jobTitle>SOA Developer</jobTitle>
   <maxSalary>20000</maxSalary>
   <minSalary>18000</minSalary>
</ns0:jobSDO>

>>> Converting element to SDO...
DOM element converted to SDO: soadev.hrservice.model.JobSDOImpl@1ee148b
SOADEV
SOA Developer
20000
18000
----End testConvertSdoToDomElementAndViceVersa----
Process exited with exit code 0.

Monday, August 1, 2011

POJO TO SDO Marshalling through JAXBHelperContext

In previous post, I demonstrated, how to easily convert pojo's to xml elements and vice-versa thru JAXB. The key to implementation in that post was the JAXBContext object which served as the factory for the Marshaller and Unmarshalller objects which accordingly takes charge for the marshalling and unmarshalling of POJO's to XML. In this post, I will take a step further by demostrating POJO to SDO marshalling through the help of JAXBHelperContext, the POJO/SDO bridge.


The JAXBHelperContext is a bridge between POJOs and SDO DataObjects. This bridge allows POJO data to be dynamically exposed as SDO DataObjects without the need to implement SDO interfaces and implementaions for each entity. Prior to this JAXBHelperContext, if you need to convert a pojo Job object to its SDO equivalent, aside from the pojo itself, you also need to have JobSDO interface, a JobSDOImpl implementation class, a JobSDO.xsd schema, and bunch of methods to handle the marshalling between your pojo and the SDO class. But with JAXBHelperContext, you only need the JobSDO.xsd schema.


Below is a sample pojo Job.java:
package soadev.hrservice.model;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlType(name = "JobSDO", namespace = "/soadev.hrservice.model/")
@XmlRootElement(name = "jobSDO")
@XmlAccessorType(XmlAccessType.FIELD)
public class Job{
    private String jobId;
    private String jobTitle;
    private Long maxSalary;
    private Long minSalary;
    //getters and setters
and the corresponding JobSDO.xsd schema definition
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
            targetNamespace="/soadev.hrservice.model/"
            xmlns="/soadev.hrservice.model/" xmlns:sdoJava="commonj.sdo/java"
            sdoJava:package="soadev.hrservice.model">
   <xsd:import namespace="commonj.sdo/java"/>
   <xsd:complexType name="JobSDO">
      <xsd:sequence>
         <xsd:element name="jobId" type="xsd:string" minOccurs="0"/>
         <xsd:element name="jobTitle" type="xsd:string" minOccurs="0"/>
         <xsd:element name="maxSalary" type="xsd:long" minOccurs="0"/>
         <xsd:element name="minSalary" type="xsd:long" minOccurs="0"/>
      </xsd:sequence>
   </xsd:complexType>
   <xsd:element name="jobSDO" type="JobSDO"/>
</xsd:schema>
The @XmlType(name = "JobSDO", namespace = "/soadev.hrservice.model/") should map accordingly to the schema definition as color coded above. The corresponding field names in the pojo should should also map to the elements in the schema. In case a field is not the same with the schema element name then you could put an @XmlElement(name="elementName") annotation on top of the field declaration to map it to the approriate element.

Action speaks louder than words...

The runner class below demonstrate how to instantiate SDO instances from POJO and schema definition and how to wrap and unwrap POJO's to SDO and vice-versa. I was amazed to note that any changes made to pojo instance is also being reflected on the SDO wrapper- well, the SDO is just wrapping the pojo anyway. :D
package soadev.sdojaxbsample.clients;

import commonj.sdo.DataObject;
import commonj.sdo.Type;
import commonj.sdo.helper.XMLDocument;
import java.sql.Timestamp;
import java.util.Date;
import javax.xml.bind.JAXBContext;
import org.eclipse.persistence.sdo.helper.jaxb.JAXBHelperContext;
import soadev.hrservice.model.Department;
import soadev.hrservice.model.Employee;
import soadev.hrservice.model.Job;

public class PojoToSdoClient {
    public static void main(String[] args) {
        PojoToSdoClient client = new PojoToSdoClient();
        try {
            client.testInstantiateSimpleSdo();
            client.testInstantiateComplexSdo();
            client.testTheAmazingWrappingPojoToSdo();
            client.testTheAmazingUnwrappingSdoToPojo();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void testInstantiateSimpleSdo()throws Exception{
        System.out.println("----Start testInstantiateSimpleSdo----");
        JAXBContext jaxbContext = JAXBContext.newInstance(Job.class);
        JAXBHelperContext jaxbHelperContext = new JAXBHelperContext(jaxbContext);  
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        jaxbHelperContext.getXSDHelper().define(loader.getResourceAsStream("soadev/hrservice/model/JobSDO.xsd"), "soadev/hrservice/model");
        Type jobType = jaxbHelperContext.getType(Job.class);
        System.out.println("Instantiating dataobject...");
        DataObject jobSDO = jaxbHelperContext.getDataFactory().create(jobType);
        System.out.println("JobSDO instantiated: " + jobSDO);
        jobSDO.set("jobId", "SOADEV");
        jobSDO.set("jobTitle", "SOA Developer");
        jobSDO.set("maxSalary", 20000L);
        jobSDO.set("minSalary", 18000L);
        System.out.println("jobTitle: " +jobSDO.getString("jobTitle"));
        //print out
        XMLDocument xmlDocument =jaxbHelperContext.getXMLHelper().createDocument(jobSDO, jobSDO.getType().getURI(), "jobSDO");
        jaxbHelperContext.getXMLHelper().save(xmlDocument, System.out, null);
        System.out.println("----End testInstantiateSimpleSdo----");
    }
    
    
    public void testInstantiateComplexSdo()throws Exception{
        System.out.println("----Start testInstantiateComplexSdo----");
        JAXBContext jaxbContext = JAXBContext.newInstance(Employee.class);
        JAXBHelperContext jaxbHelperContext = new JAXBHelperContext(jaxbContext);  
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        jaxbHelperContext.getXSDHelper().define(loader.getResourceAsStream("soadev/hrservice/model/JobSDO.xsd"), "soadev/hrservice/model");
        jaxbHelperContext.getXSDHelper().define(loader.getResourceAsStream("soadev/hrservice/model/DepartmentSDO.xsd"), "soadev/hrservice/model");
        jaxbHelperContext.getXSDHelper().define(loader.getResourceAsStream("soadev/hrservice/model/EmployeeSDO.xsd"), "soadev/hrservice/model");
        Type employeeType = jaxbHelperContext.getType(Employee.class);
        System.out.println("Instantiating EmployeeSDO...");
        DataObject employeeSDO = jaxbHelperContext.getDataFactory().create(employeeType);
        System.out.println("EmployeeSDO instantiated: " + employeeSDO);
        employeeSDO.set("firstName", "Rommel");
        employeeSDO.set("lastName", "Pino");
        Type jobType = jaxbHelperContext.getType(Job.class);
        System.out.println("Instantiating JobSDO...");
        DataObject jobSDO = jaxbHelperContext.getDataFactory().create(jobType);
        System.out.println("JobSDO instantiated: " + jobSDO);
        jobSDO.set("jobId", "SOADEV");
        System.out.println("Setting the job property of the EmployeeSDO object");
        employeeSDO.set("job", jobSDO);
        Type departmentType = jaxbHelperContext.getType(Department.class);
        System.out.println("Instantiating DepartmentSDO...");
        DataObject departmentSDO = jaxbHelperContext.getDataFactory().create(departmentType);
        System.out.println("DepartmentSDO instantiated: " + departmentSDO);
        departmentSDO.set("departmentName", "Java Development Team");
        System.out.println("Setting the department property of the EmployeeSDO object");
        employeeSDO.set("department", departmentSDO);
        System.out.println("Retrieving the jobId property from the EmployeeSDO object");
        System.out.println("jobId is: " + employeeSDO.get("job/jobId"));
        System.out.println("Retrieving the departmentName property from the EmployeeSDO object");
        System.out.println("departmentName is: " + employeeSDO.get("department/departmentName"));
        //print out
        XMLDocument xmlDocument =jaxbHelperContext.getXMLHelper().createDocument(employeeSDO, employeeSDO.getType().getURI(), "employeeSDO");
        jaxbHelperContext.getXMLHelper().save(xmlDocument, System.out, null);
        System.out.println("----End testInstantiateComplexSdo----");
    }
    
    public void testTheAmazingWrappingPojoToSdo()throws Exception{
        System.out.println("----Start testTheAmazingWrappingPojoToSdo----");
        Employee employee = new Employee();
        employee.setLastName("Pino");
        employee.setFirstName("Rommel");
        employee.setHireDate(new Timestamp(new Date().getTime()));
        Job job = new Job();
        job.setJobId("SOADEV");
        job.setJobTitle("SOA Developer");
        job.setMaxSalary(20000L);
        job.setMinSalary(18000L);
        employee.setJob(job);
        Department department = new Department();
        department.setDepartmentId(100L);
        department.setDepartmentName("Java Development Team");
        employee.setDepartment(department);
        JAXBContext jaxbContext = JAXBContext.newInstance(Employee.class);
        JAXBHelperContext jaxbHelperContext = new JAXBHelperContext(jaxbContext); 
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        jaxbHelperContext.getXSDHelper().define(loader.getResourceAsStream("soadev/hrservice/model/JobSDO.xsd"), "soadev/hrservice/model");
        jaxbHelperContext.getXSDHelper().define(loader.getResourceAsStream("soadev/hrservice/model/DepartmentSDO.xsd"), "soadev/hrservice/model");
        jaxbHelperContext.getXSDHelper().define(loader.getResourceAsStream("soadev/hrservice/model/EmployeeSDO.xsd"), "soadev/hrservice/model");
        DataObject employeeSDO = jaxbHelperContext.wrap(employee);
        //print out
        XMLDocument xmlDocument =jaxbHelperContext.getXMLHelper().createDocument(employeeSDO, employeeSDO.getType().getURI(), "employeeSDO");
        jaxbHelperContext.getXMLHelper().save(xmlDocument, System.out, null);
        System.out.println("----End testTheAmazingWrappingPojoToSdo----");
    }
    
    public void testTheAmazingUnwrappingSdoToPojo()throws Exception{
        System.out.println("----Start testTheAmazingUnwrappingSdoToPojo----");
        JAXBContext jaxbContext = JAXBContext.newInstance(Job.class);
        JAXBHelperContext jaxbHelperContext = new JAXBHelperContext(jaxbContext);
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        jaxbHelperContext.getXSDHelper().define(loader.getResourceAsStream("soadev/hrservice/model/JobSDO.xsd"), "soadev/hrservice/model");
        Type jobType = jaxbHelperContext.getType(Job.class);
        DataObject jobSDO = jaxbHelperContext.getDataFactory().create(jobType);
        jobSDO.set("jobTitle", "SOA Developer");
        System.out.println("Unwrapping jobSDO to job Pojo...");
        Job job = (Job) jaxbHelperContext.unwrap(jobSDO);
        System.out.println(job);
        System.out.println(job.getJobTitle());
        job.setJobId("SOADEV");
        System.out.println("Changes to Pojo is also reflected in the SDO wrapper...");
        System.out.println(jobSDO.getString("jobId"));
        System.out.println("----End testTheAmazingUnwrappingSdoToPojo----");
    }
}
Run output...

C:\Oracle\11g\jdk160_24\bin\javaw.exe -client -classpath ...
----Start testInstantiateSimpleSdo----
Instantiating dataobject...
JobSDO instantiated: soadev.hrservice.model.JobSDOImpl@2c35e
jobTitle: SOA Developer
<?xml version="1.0" encoding="UTF-8"?>
<ns0:jobSDO xmlns:ns0="/soadev.hrservice.model/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <jobId>SOADEV</jobId>
   <jobTitle>SOA Developer</jobTitle>
   <maxSalary>20000</maxSalary>
   <minSalary>18000</minSalary>
</ns0:jobSDO>
----End testInstantiateSimpleSdo----
----Start testInstantiateComplexSdo----
[EL Warning]: 2011-08-01 16:32:28.683--An java.lang.IllegalArgumentException occurred processing referenced schema with uri /soadev.hrservice.model/ with schemaLocation DepartmentSDO.xsd.
[EL Warning]: 2011-08-01 16:32:28.699--An java.lang.IllegalArgumentException occurred processing referenced schema with uri /soadev.hrservice.model/ with schemaLocation JobSDO.xsd.
Instantiating EmployeeSDO...
EmployeeSDO instantiated: soadev.hrservice.model.EmployeeSDOImpl@18a8ce2
Instantiating JobSDO...
JobSDO instantiated: soadev.hrservice.model.JobSDOImpl@c943d1
Setting the job property of the EmployeeSDO object
Instantiating DepartmentSDO...
DepartmentSDO instantiated: soadev.hrservice.model.DepartmentSDOImpl@b40ec4
Setting the department property of the EmployeeSDO object
Retrieving the jobId property from the EmployeeSDO object
jobId is: SOADEV
Retrieving the departmentName property from the EmployeeSDO object
departmentName is: Java Development Team
<?xml version="1.0" encoding="UTF-8"?>
<ns0:employeeSDO xmlns:ns0="/soadev.hrservice.model/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <department>
      <departmentName>Java Development Team</departmentName>
   </department>
   <firstName>Rommel</firstName>
   <job>
      <jobId>SOADEV</jobId>
   </job>
   <lastName>Pino</lastName>
</ns0:employeeSDO>
----End testInstantiateComplexSdo----
----Start testTheAmazingWrappingPojoToSdo----
[EL Warning]: 2011-08-01 16:32:28.762--An java.lang.IllegalArgumentException occurred processing referenced schema with uri /soadev.hrservice.model/ with schemaLocation DepartmentSDO.xsd.
[EL Warning]: 2011-08-01 16:32:28.762--An java.lang.IllegalArgumentException occurred processing referenced schema with uri /soadev.hrservice.model/ with schemaLocation JobSDO.xsd.
<?xml version="1.0" encoding="UTF-8"?>
<ns0:employeeSDO xmlns:ns0="/soadev.hrservice.model/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <department>
      <departmentId>100</departmentId>
      <departmentName>Java Development Team</departmentName>
   </department>
   <firstName>Rommel</firstName>
   <hireDate>2011-08-01T13:32:28.699Z</hireDate>
   <job>
      <jobId>SOADEV</jobId>
      <jobTitle>SOA Developer</jobTitle>
      <maxSalary>20000</maxSalary>
      <minSalary>18000</minSalary>
   </job>
   <lastName>Pino</lastName>
</ns0:employeeSDO>
----End testTheAmazingWrappingPojoToSdo----
----Start testTheAmazingUnwrappingSdoToPojo----
Unwrapping jobSDO to job Pojo...
soadev.hrservice.model.Job@24c4a3
SOA Developer
Changes to Pojo is also reflected in the SDO wrapper...
SOADEV
----End testTheAmazingUnwrappingSdoToPojo----
Process exited with exit code 0.

Sample Application

You can access the sample application from here.

Common Gotchas

If you encounter "ClassCastException com.sun.xml.bind.v2.runtime.JAXBContextImpl cannot be cast to org.eclipse.persistence.jaxb.JAXBContext" when you run your own sample, you need to specify EclipseLink MOXy as your JAXB provider by adding a file called jaxb.properties in the same package as your model classes. Please see the sample file in the application.

Relevant Links