Sunday, July 31, 2011

Sample JAXB Application Converting POJO to XML Element and Vice-versa

In developing SOA applications, it is necessary that you have tools in converting Java objects to XML and vice-versa. In Oracle Human Task Services for example, the task payloads are represented as XML elements, and if there's a need to manipulate those payload then, I believe that its better to do the manipulation on a Java instance equivalent, rather than navigating to the complex tree structure of the XML payload. With the Java instance, you could take advantage of Java's vast frameworks and utilities.


In this post, I will demonstrate how to convert POJO's to XML and vice-versa using Java Architecture for XML Binding (JAXB), and will give some tips in annotating our entities to avoid some common gotchas.

To be able to convert your POJO to XML, it is mandatory that you put an "@XmlRootElement" annotation to your target classes.
package soadev.jaxbsample.model;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "job")
@XmlAccessorType(XmlAccessType.FIELD)
public class Job{
    private String jobId;
    private String jobTitle;
    private Long maxSalary;
    private Long minSalary;
    //getters and setters
The root name "job" above will be the root of the generated XML as follows:
<job>
   <jobId></jobId>
   <jobTitle></jobTitle>
   <maxSalary></maxSalary>
   <minSalary></minSalary>
</job>

The "@XmlAccessorType(XmlAccessType.FIELD)" annotation tells JAXB that only the fields will be marshalled to XML. There are other types which you can read from the links near the end of this post. I am more inclined to use this annotation particularly because our entities persistence annotations are also on the fields, and that we have a lot of transient methods like "computeTotal" which value I don't need to be marshalled.

I will also recommend that you removed any unnecessary associations (some auto-generated by JDeveloper when creating entities from database tables)like for example departmentList in the Employee class (mapped by the manager in Departments). Those associations will add too much complexity especially if you are already starting to expose your EJB session beans as Web Services or as EJB Service. When you cannot remove it due to some usage requirement then put "@XmlTransient" annotation if those associations are not necessary to be marshalled to XML.

If you have fields of type "java.sql.Timestamp", create a TimeStampAdapter class as follows:

package soadev.jaxb.adapters;

import java.sql.Timestamp;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class TimestampAdapter extends XmlAdapter<Date, Timestamp> {

    public Date marshal(Timestamp value) {
        if(value == null){
            return null;
        }
        return new Date(value.getTime());
    }

    public Timestamp unmarshal(Date value) {
        if (value == null){
            return null;
        }
        return new Timestamp(value.getTime());
    }
}
...and annotate your approriate fields as follows:
    @XmlJavaTypeAdapter(value = TimestampAdapter.class)
    private Timestamp hireDate;
This annotation can also be put inside a "package-info.java". Please see the links for details.

The runner class

The class below shows how to convert POJO to XML. It also contains a sample on how to convert to POJO an XML element with a different root name.

package soadev.jaxbsample.clients;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import oracle.bpel.services.common.util.XMLUtil;

import org.w3c.dom.Document;

import org.w3c.dom.Element;

import soadev.jaxbsample.model.Department;
import soadev.jaxbsample.model.Employee;
import soadev.jaxbsample.model.Job;

public class SimpleJaxbClient {
    public static void main(String[] args) {
        SimpleJaxbClient client = new SimpleJaxbClient();
        try {
            client.testMarshallUnmarshallSimplePojoWithSameRootQName();
            client.testMarshallUnmarshallComplexPojoWithSameRootQName();
            client.testUnmarshallComplexXmlWithDifferentRootQName();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void testMarshallUnmarshallSimplePojoWithSameRootQName() throws Exception {
        System.out.println("----Start testMarshallUnmarshallSimplePojoWithSameRootQName----");
        JAXBContext context = JAXBContext.newInstance(Job.class);
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        System.out.println("Instantiating simple pojo...");
        Job job = new Job();
        job.setJobId("SOADEV");
        job.setJobTitle("SOA Developer");
        job.setMaxSalary(10000L);
        job.setMinSalary(8000L);
        System.out.println("Simple pojo created: " + job);
        System.out.println("Marshalling pojo to a DOM Node...");
        DocumentBuilder docBuilder =
            DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document document = docBuilder.newDocument();
        marshaller.marshal(job, document);
        Element jobElement = document.getDocumentElement();
        System.out.println("Job element created: " + jobElement);
        System.out.println(XMLUtil.toString(jobElement));
        System.out.println("Unmarshalling an xml element to a POJO");
        Unmarshaller unmarshaller = context.createUnmarshaller();
        Job jobPojoFromXml = (Job)unmarshaller.unmarshal(jobElement);
        System.out.println("Simple pojo from xml created :" + jobPojoFromXml);
        System.out.println(jobPojoFromXml);
        System.out.println(jobPojoFromXml.getJobTitle());
        System.out.println(jobPojoFromXml.getJobId());
        System.out.println("----End testMarshallUnmarshallSimplePojoWithSameRootQName----");
    }
    
    public void testMarshallUnmarshallComplexPojoWithSameRootQName()throws Exception{
        System.out.println("----Start testMarshallUnmarshallComplexPojoWithSameRootQName----");
        JAXBContext context = JAXBContext.newInstance(Employee.class);
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        System.out.println("Instantiating complex pojo...");
        Employee employee = new Employee();
        employee.setEmployeeId(1L);
        employee.setFirstName("Rommel");
        employee.setLastName("Pino");
        Job job = new Job();
        job.setJobId("SOADEV");
        job.setJobTitle("SOA Developer");
        job.setMaxSalary(10000L);
        job.setMinSalary(5000L);
        employee.setJob(job);
        Department department = new Department();
        department.setDepartmentName("Java Team");
        employee.setDepartment(department);
        System.out.println("Complex pojo created: " + employee);
        System.out.println("Marshalling complex pojo to a DOM Node...");
        DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document document = docBuilder.newDocument();
        marshaller.marshal(employee, document);
        Element employeeElement = document.getDocumentElement();
        System.out.println("Employee element created: "+ employeeElement);
        System.out.println(XMLUtil.toString(employeeElement));
        System.out.println("Unmarshalling an xml element to a POJO");
        Unmarshaller unmarshaller = context.createUnmarshaller();
        Employee employeePojoFromXml = (Employee)unmarshaller.unmarshal(employeeElement);
        System.out.println("Complex pojo from xml created :" + employeePojoFromXml);
        System.out.println(employeePojoFromXml);
        System.out.println(employeePojoFromXml.getEmployeeId());
        System.out.println(employeePojoFromXml.getFirstName());
        System.out.println(employeePojoFromXml.getLastName());
        System.out.println(employeePojoFromXml.getJob());
        System.out.println(employeePojoFromXml.getJob().getJobTitle());
        System.out.println(employeePojoFromXml.getDepartment());
        System.out.println(employeePojoFromXml.getDepartment().getDepartmentName());
        System.out.println("----End testMarshallUnmarshallComplexPojoWithSameRootQName----");
    }
    
    public void testUnmarshallComplexXmlWithDifferentRootQName()throws Exception{
        System.out.println("----Start testUnmarshallComplexXmlWithDifferentRootQName----");
        //root is 'employeeVariable' instead of 'employee'
        String xmlString = 
            "<employeeVariable>" + 
            "  <employeeId>1</employeeId>" + 
            "  <firstName>Rommel</firstName>" + 
            "  <lastName>Pino</lastName>" + 
            "  <job>" + 
            "    <jobId>SOADEV</jobId>" + 
            "    <jobTitle>SOA Developer</jobTitle>" + 
            "    <maxSalary>10000</maxSalary>" + 
            "    <minSalary>5000</minSalary>" + 
            "  </job>" + 
            "  <department>" +
            "    <departmentName>Java Team</departmentName>" +
            "  </department>" +         
            "</employeeVariable>";
        InputStream is = new ByteArrayInputStream(xmlString.getBytes());
        DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document document = docBuilder.parse(is);
        Element varElement = document.getDocumentElement();
        JAXBContext context = JAXBContext.newInstance(Employee.class);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        JAXBElement<Employee> employeeJaxbElement = unmarshaller.unmarshal(varElement, Employee.class);
        System.out.println("Element variable is :" + employeeJaxbElement.getName());
        Employee employeePojoFromXml = employeeJaxbElement.getValue();
        System.out.println("Complex pojo from xml created :" + employeePojoFromXml);
        System.out.println(employeePojoFromXml);
        System.out.println(employeePojoFromXml.getEmployeeId());
        System.out.println(employeePojoFromXml.getFirstName());
        System.out.println(employeePojoFromXml.getLastName());
        System.out.println(employeePojoFromXml.getJob());
        System.out.println(employeePojoFromXml.getJob().getJobTitle());
        System.out.println(employeePojoFromXml.getDepartment());
        System.out.println(employeePojoFromXml.getDepartment().getDepartmentName());
        System.out.println("----End testUnmarshallComplexXmlWithDifferentRootQName----");
    }    
}
Run Outpout
C:\Oracle\11g\jdk160_24\bin\javaw.exe -client -classpath ...
----Start testMarshallUnmarshallSimplePojoWithSameRootQName----
Instantiating simple pojo...
Simple pojo created: soadev.jaxbsample.model.Job@a6d51e
Marshalling pojo to a DOM Node...
Job element created: oracle.xml.parser.v2.XMLElement@9f5011
<job>
   <jobId>SOADEV</jobId>
   <jobTitle>SOA Developer</jobTitle>
   <maxSalary>10000</maxSalary>
   <minSalary>8000</minSalary>
</job>

Unmarshalling an xml element to a POJO
Simple pojo from xml created :soadev.jaxbsample.model.Job@c62c8
soadev.jaxbsample.model.Job@c62c8
SOA Developer
SOADEV
----End testMarshallUnmarshallSimplePojoWithSameRootQName----
----Start testMarshallUnmarshallComplexPojoWithSameRootQName----
Instantiating complex pojo...
Complex pojo created: soadev.jaxbsample.model.Employee@76fba0
Marshalling complex pojo to a DOM Node...
Employee element created: oracle.xml.parser.v2.XMLElement@949f69
<employee>
   <department>
      <departmentName>Java Team</departmentName>
   </department>
   <employeeId>1</employeeId>
   <firstName>Rommel</firstName>
   <lastName>Pino</lastName>
   <job>
      <jobId>SOADEV</jobId>
      <jobTitle>SOA Developer</jobTitle>
      <maxSalary>10000</maxSalary>
      <minSalary>5000</minSalary>
   </job>
</employee>

Unmarshalling an xml element to a POJO
Complex pojo from xml created :soadev.jaxbsample.model.Employee@82764b
soadev.jaxbsample.model.Employee@82764b
1
Rommel
Pino
soadev.jaxbsample.model.Job@12452e8
SOA Developer
soadev.jaxbsample.model.Department@1bf3d87
Java Team
----End testMarshallUnmarshallComplexPojoWithSameRootQName----
----Start testUnmarshallComplexXmlWithDifferentRootQName----
Element variable is :employeeVariable
Complex pojo from xml created :soadev.jaxbsample.model.Employee@42552c
soadev.jaxbsample.model.Employee@42552c
1
Rommel
Pino
soadev.jaxbsample.model.Job@e5bbd6
SOA Developer
soadev.jaxbsample.model.Department@8ee016
Java Team
----End testUnmarshallComplexXmlWithDifferentRootQName----
Process exited with exit code 0.

Sample Application

You can get the sample application from here. To run, find the SimpleJaxbClient.java and run.

Relevant links

  • JAXB Architecture
  • Thursday, July 28, 2011

    Custom BPM Workspace Deployed in a Non-SOA Server in a Different Domain

    In my previous post, I demonstrated through a sample application how to utilize the reusable task flows from BPM Workspace. In this post, I will share the steps and configuration to deploy the custom application to a non-soa server located in a different domain.

    The soa server in my case is a stand alone installation of Oracle SOA Suite with configured domain "base_domain" that supports SOA, BPM, and BAM.
    The remote non-soa server is the default integrated weblogic server of JDeveloper using port "7101". The integrated weblogic server "DefaultServer" is in domain "DefaultDomain".

    Steps and Configuration

    There are four steps to successfully run the custom application:
    1. Set the "federatedMode" input parameter of the taskList task flow to true.
    2. Deploy all the referred libraries in the weblogic-application.xml and weblogic.xml to the non-soa server as follows:
      • oracle.soa.bpel
      • oracle.soa.workflow
      • oracle.bpm.runtime
      • oracle.bpm.client
      • oracle.bpm.projectlib
      • oracle.bpm.workspace
      • oracle.bpm.webapp.common
      • oracle.soa.worklist.webapp
      Note: If you are not using in your custom application the BPM related task flows but the task list, then you just need to deploy the oracle.soa.workflow.jar and oracle.soa.worklist.webapp.jar. These libraries are present in the sub directories in "$MIDDLEWARE_HOME$\jdeveloper\soa\modules".

    3. Add a properly configured wf_client_config.xml in the classpath. You can put the file inside "MyUIShellBPMApp\UIShellBPMWeb\src" directory. A sample wf_client_config.xml is as follows:
      <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
      <workflowServicesClientConfiguration  xmlns="http://xmlns.oracle.com/bpel/services/client" clientType="REMOTE">
         <server default="true" name="default">
            <localClient>
               <participateInClientTransaction>false</participateInClientTransaction>
            </localClient>
            <remoteClient>
               <serverURL>t3://hoitpino:8001</serverURL>
               <initialContextFactory>weblogic.jndi.WLInitialContextFactory</initialContextFactory>
               <participateInClientTransaction>false</participateInClientTransaction>
            </remoteClient>
            <soapClient>
               <rootEndPointURL>http://hoitpino:8001</rootEndPointURL>
               <identityPropagation mode="dynamic" type="saml">
                  <policy-references>
                     <policy-reference enabled="true" category="security" 
                      uri="oracle/wss10_saml_token_client_policy"/>
                  </policy-references>
               </identityPropagation>
            </soapClient>
         </server>
      </workflowServicesClientConfiguration>
      
      
    4. Establish global trust between the domains.
      1. Login to the Oracle WebLogic Server console.
      2. Under Domain Structures, select the domain name like "base_domain".
      3. Select the Security tab.
      4. Click the Advanced link (near the bottom Save button.)
      5. Give some password in the "Credential" field. The same password should be used for the other domains.
      6. Click Save.
      7. Restart the server.

    Running the application

    Access the sample application from my previous post and follow the steps described above. Run the application by right clicking the main.jspx in JDeveloper, then run.

    Below is a screenshot of my running application in integrated weblogic server:

    Cheers!