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

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!

    Wednesday, July 27, 2011

    Custom ADF UIShell Application with Oracle BPM Workspace Task Flows

    An Excerpt from Appendix A (Creating Custom ADF Applications with Oracle Business Process Management Workspace Task Flows) of the Oracle Fusion Middleware User's Guide for Oracle Business Process Management 11g Release 1 (11.1.1.5.0)
    Different features available in Process Workspace are exposed as standalone reusable components, called task flows. You can embed task flows in any Oracle Application Development Framework (ADF) application. These standalone task flows provide many parameters that enable you to build customized applications.
    All the task flows are bundled in an ADF library that you can include in the application in which you are embedding.

    Below are the notable steps that I have done to incorporate the BPM Workspace task flows into this custom ADF UI Shell application:
    1. I Added the BPM Worklist component in the class path. This library includes the adflibTaskListTaskFlow.jar that contains the reusable taskflows related to the standard worklist application.
    2. I want to display the "Process Tracking" and "Standard Dashboards" tabs, so I acquired the oracle.bpm.workspace-adflib.jar from a process portal installation and added it into the libraries and classpath.
    3. Modified the weblogic-application.xml to add some soa and bpm related library references. Please see the weblogic-application.xml in the artifacts section below.
    4. Added weblogic.xml descriptor that includes a library ref to the "oracle.soa.worklist.webapp" library. Please see artifacts section for the complete weblogic.xml
    5. With the jars configures from the steps above, the application is already deploying fine, but I encountered ClassNotFoundExceptions during runtime. I exploded the OracleBPMWorkspace application that ships with the standard Oracle Suite 11g R1 installation and noticed 4 additonal jars as part of the WEB-INF/lib directory, namely: oracle.bpm.security.jar; oracle.bpm.jsfcomponents.jar; oracle.bpm.workspace.adf.jar; oracle.bpm.workspace.model.jar; and oracle.bpm.web-resources.jar. I added them all to the libraries and class path.
    6. I configured ADF Security and added resource grants to the corresponding task flows. Ensure to mark check the "Show task flows imported from ADF libraries" checkbox located in the jaz-data.xml Overview tab.
    7. Tested the application by deploying it to a soa enabled weblogic server.

    User Experience

    Figure 1: Workspace Tasks Tab

    To display the BPM Applications/ Initiable tasks in the left panel above, you also need to set the "taskFlowMode" parameter of the "taskList-task-flow-definition" to "workspace" and add the taskFlow binding "processApplicationsTaskflow1" on the same page definition. Please the see the complete taskListPageDef.xml page definition on the artifacts section below.


    Figure 2: Workspace Process Tracking Tab

    The process instances table is displaying, except for ONE BIG FAIL, it does not expose anything, like sort of contextual events- for example, so that the processInstanceDetailTaskflow can sync with the current selection on the processInstancesTaskflow. I hope I'm just wrong but the documentation is empty about this.


    Figure 3: Workspace Standard Dashboards Tab


    Artifacts

    weblogic-application.xml
    <?xml version = '1.0' encoding = 'windows-1252'?>
    <weblogic-application xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                          xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-application http://www.bea.com/ns/weblogic/weblogic-application/1.0/weblogic-application.xsd"
                          xmlns="http://www.bea.com/ns/weblogic/weblogic-application">
      <listener>
        <listener-class>oracle.adf.share.weblogic.listeners.ADFApplicationLifecycleListener</listener-class>
      </listener>
      <listener>
        <listener-class>oracle.mds.lcm.weblogic.WLLifecycleListener</listener-class>
      </listener>
      <library-ref>
        <library-name>adf.oracle.domain</library-name>
      </library-ref>
      <library-ref>
        <library-name>oracle.soa.bpel</library-name>
      </library-ref>
      <library-ref>
        <library-name>oracle.soa.workflow</library-name>
      </library-ref>
      <library-ref>
        <library-name>oracle.bpm.runtime</library-name>
      </library-ref>
      <library-ref>
        <library-name>oracle.bpm.client</library-name>
      </library-ref>
      <library-ref>
        <library-name>oracle.bpm.projectlib</library-name>
      </library-ref>
      <library-ref>
        <library-name>oracle.bpm.workspace</library-name>
      </library-ref>
      <library-ref>
        <library-name>oracle.bpm.webapp.common</library-name>
      </library-ref>
    </weblogic-application>
    
    

    weblogic.xml
    <?xml version = '1.0' encoding = 'windows-1252'?>
    <weblogic-web-app xmlns="http://www.bea.com/ns/weblogic/weblogic-web-app">
    
      <security-role-assignment>
        <role-name>valid-users</role-name>
        <principal-name>users</principal-name>
      </security-role-assignment>
      <session-descriptor>
        <persistent-store-type>replicated_if_clustered</persistent-store-type>
      </session-descriptor>
      <library-ref>
        <library-name>oracle.soa.worklist.webapp</library-name>
        <specification-version>11.1.1</specification-version>
      </library-ref>
    </weblogic-web-app>
    

    taskListPageDef.xml
    <?xml version="1.0" encoding="UTF-8" ?>
    <pageDefinition xmlns="http://xmlns.oracle.com/adfm/uimodel"
                    version="11.1.1.60.13" id="taskListPageDef"
                    Package="fragments.workspace">
      <parameters/>
      <executables>
        <variableIterator id="variables"/>
        <taskFlow id="taskListtaskflowdefinition1"
                  taskFlowId="/WEB-INF/taskList-task-flow-definition.xml#taskList-task-flow-definition"
                  activation="deferred"
                  xmlns="http://xmlns.oracle.com/adf/controller/binding">
          <parameters>
            <parameter id="taskFlowMode" value="workspace"/>
            <parameter id="showViewsPanel" value="true"/>
            <parameter id="showTaskDetailsPanel" value="true"/>
          </parameters>
        </taskFlow>
        <taskFlow id="processApplicationsTaskflow1"
                  taskFlowId="/WEB-INF/processApplicationsTaskflow.xml#processApplicationsTaskflow"
                  activation="deferred"
                  xmlns="http://xmlns.oracle.com/adf/controller/binding"/>
      </executables>
      <bindings/>
    </pageDefinition>
    

    Sample Application

    You can download the sample sample application from here.
    To test the sample application after deployment, open in browser "http://host:port/myworkspace/faces/main".

    Saturday, July 23, 2011

    How to Send Notification to the Initiator of a BPM Process Instance Upon Completion of an Activity

    In BPEL, human workflow is almost always expected to be represented by a single human task activity with multiple steps - that is why the Oracle SOA Suite 11g Human Task component has a lot of facility for notification related to the raise of events like the assign, error, expire, withdraw, alert, complete, and etc. on a particular task definition.

    But in the current release of BPM (11.1.1.5), it is different. Each task participant or steps in the workflow are represented by a different task definition, and to notify the initiator of a process instance, upon approval or disapproval of a request or a completion of a certain activity, needs to be configured on a separate service call activity, and definitely, not covered by the facilities of Human Task component.

    And there goes the topic of this blog post - How to send notification to the initiator of a BPM process instance upon completion of an activity?

    First, of course you need to have a properly set-up email driver configuration. If you still don't, then you could take advantage of gmail as the smtp server as describe on my previous post - Using GMail as Mail Server for Oracle SOA Suite 11g Notifications.

    Secondly, you need to obtain the the necessary wsdl and xsd files of the NotificationService available from BPEL. This is describe in one of Niall Commiskey's charming blog post - BPM 11g and Notifications.

    Third, we need to get the email address of the initiator of a bpm process instance so we can set it as the addressee on a NotificationService service call. This is the thing that will be described on this post ;)

    Before we can get the email address on the initiator, we need to know the username of the initiator first. My first bet was to use the BPM Extension Function - "bpmn:getProcessInstanceAttribute('creator')", but this doesn't seem to work as of 11.1.1.5 in which I am getting some internal exceptions that says - "the variable is not defined in the current scope". As an alternative, the initiator username can be acquired through the following xpath expression available from BPEL Xpath Extension Functions:
        ora:getCreator()
    
    To get email address of a specified user we can use the following xpath expression from the Identity Service Functions:
        ids:getUserProperty()
    
    Excerpt from the expression builder:
    Get a user property. The signature of this function is ids:getUserProperty(userName, attributeName, realmName) The arguments to the function: 1) userName - String or element containing the user whose attribute should be retrieved 1) attributeName - String or element containing the name of the user attribute. The attribute name should be one of the following values: givenName middleName sn displayName mail telephoneNumber homephone mobile facsimileTelephoneNumber pager preferredLanguage title manager 3)realmName - The realm name. This is optional and if not specified default realm is assumed.

    And finally, below is the compound expression to access the email address of the initiator of the process instance:
        ids:getUserProperty(ora:getCreator(),'mail')
    
    Cheers!

    Thursday, July 21, 2011

    Custom BPM Applications(InitiableTasks) ADF TaskFlow

    In a previous post, we show how to access the initiable tasks in a main method of a simple Java class. In line with, this post demonstrates how to get the task's URL and launch the corresponding generated BPM task details form in an ADF application.


    The following are the key methods and artifacts:
    • getTaskDisplayURL() - this method retrieves from a task object, the URL defined in the runtime Human Task configuration of a specific deployed composite, which can be found in Enterprise Manager. The URL is automatically set when you deploy a web app that contains BPM task forms with the configuration - "hwtaskflow.xml" that contains our task definition.
    • getTaskFlowDefinitionId() - this method returns a TaskFlowId object from the lengthy URL returned by the getTaskDisplayURL() method above.
    • initiateTask() - this method that returns a string is used as action attribute in the page fragment. It invokes both methods above then returns a "callTaskDetail" which launches a declarative popup.
    • The declarative pop-up, "modal-dialog-task-flow", is a simple task flow that we reused from the BPM Worklist components library. It doesn't do much but invoke a remote task flow. We could create our own if necessary.
    • wf_client_config.xml - We add this config file to remove from our code the connection details. To get a BPMServiceClientFactory instance, we just passed null parameters as follows:
      factory = BPMServiceClientFactory.getInstance(null, null, null);
    • Configure ADF Security - so we don't need to pass username and password when getting a BPMContext.

    Below is the the backing bean that support our page fragment.
    package soadev.bpm.web.backing;
    
    import java.net.URLDecoder;
    
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.StringTokenizer;
    
    import oracle.adf.controller.TaskFlowId;
    
    import oracle.bpel.services.bpm.common.IBPMContext;
    import oracle.bpel.services.workflow.task.model.Task;
    import oracle.bpel.services.workflow.worklist.api.util.WorklistUtil;
    import oracle.bpel.services.workflow.worklist.servlet.Constants;
    
    import oracle.bpm.client.BPMServiceClientFactory;
    import oracle.bpm.services.client.IBPMServiceClient;
    import oracle.bpm.services.processmetadata.IProcessMetadataService;
    import oracle.bpm.services.processmetadata.ProcessMetadataSummary;
    
    import org.apache.myfaces.trinidad.context.RequestContext;
    
    import soadev.view.utils.JSFUtils;
    
    
    public class InitiableTaskListForm {
        private IBPMContext bpmContext;
        private ProcessMetadataSummary process;
        private BPMServiceClientFactory factory;
    
        public InitiableTaskListForm() {
            factory = BPMServiceClientFactory.getInstance(null, null, null);
        }
        public List<ProcessMetadataSummary> getInitiatiableTasks() {
            try {
                // get the process metadata service
                IProcessMetadataService pms =
                    factory.getBPMServiceClient().getProcessMetadataService();
                // get the list of initiatable proceses
                return pms.getInitiatableProcesses(getIBPMContext());
            } catch (Exception e) {
                e.printStackTrace();
                throw new RuntimeException(e);
            }
        }
        
        public String initiateTask() throws Exception {
            Task task = initiateTask(process.getCompositeDN()+ "/" + process.getProcessName());
            String detailsUrl = getTaskDisplayURL(task);
            getPageFlowScope().put("remoteUserTaskUrl", detailsUrl);
            getPageFlowScope().put("remoteTaskDefId", getTaskFlowDefinitionId(detailsUrl));
            return "callTaskDetail";
        }
    
    
        private Map<String, Object> getPageFlowScope() {
            return RequestContext.getCurrentInstance().getPageFlowScope();
        }
    
        private Task initiateTask(String compositeDN) throws Exception {
            IBPMServiceClient client = factory.getBPMServiceClient();
            return client.getInstanceManagementService().createProcessInstanceTask(getIBPMContext(),
                                                                                   compositeDN);
        }
    
        private String getTaskDisplayURL(Task task) throws Exception {
            Map parameters = new HashMap();
            parameters.put(Constants.BPM_WORKLIST_TASK_ID,
                           task.getSystemAttributes().getTaskId());
            parameters.put(Constants.BPM_WORKLIST_CONTEXT,
                           getIBPMContext().getToken());
            String url =
                WorklistUtil.getTaskDisplayURL(factory.getWorkflowServiceClient(),
                                               getIBPMContext(), task, null,
                                               "worklist", parameters);
            return url;
        }
    
    
        //courtesy of http://amulyamishras-tech-blog.blogspot.com/2011/01/opening-remote-http-url-inan-adf-dialog.html
    
        private TaskFlowId getTaskFlowDefinitionId(String detailsURL) {
            //Checks if the url is null.
            if (detailsURL == null)
                return null;
            //gets the parameter list from the url.
            String parameterList =
                detailsURL.substring(detailsURL.indexOf("?") + 1,
                                     detailsURL.length());
            //tokenizes the url.
            StringTokenizer stok = new StringTokenizer(parameterList, "&");
            String taskFlowId = null;
            String documentName = null;
            //gets the taskflowId from the url.
            while (stok.hasMoreTokens()) {
                String parameter = stok.nextToken();
                String[] parts = parameter.split("=", 2);
                if (parts[0].equals("_id"))
                    taskFlowId = parts[1];
                if (parts[0].equals("_document"))
                    documentName = parts[1];
            }
            //decodes the encoded url.
            try {
                documentName = URLDecoder.decode(documentName, "UTF-8");
                taskFlowId = URLDecoder.decode(taskFlowId, "UTF-8");
    
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            return new TaskFlowId(documentName, taskFlowId);
        }
    
        public void setProcess(ProcessMetadataSummary process) {
            this.process = process;
        }
    
        public ProcessMetadataSummary getProcess() {
            return process;
        }
    
        private IBPMContext getIBPMContext() throws Exception {
            if (bpmContext == null) {
                bpmContext = (IBPMContext)JSFUtils.getFromSession("BPM_CONTEXT");
            }
            if (bpmContext == null) {
                bpmContext = factory.getBPMUserAuthenticationService().getBPMContextForAuthenticatedUser();
            }
            JSFUtils.storeOnSession("BPM_CONTEXT", bpmContext);
            return bpmContext;
        }
    }
    
    The "initiable_task_list.jsff" page fragment:
    <?xml version='1.0' encoding='UTF-8'?>
    <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
              xmlns:af="http://xmlns.oracle.com/adf/faces/rich"
              xmlns:f="http://java.sun.com/jsf/core">
      <af:panelBox text="Applications" id="pb1">
        <f:facet name="toolbar"/>
        <af:panelList id="pl1">     
          <af:forEach items="#{viewScope.initiableTaskListForm.initiatiableTasks}"
                      var="process">
                <af:commandLink text="#{process.projectName}: #{process.processName}"
                          action="#{viewScope.initiableTaskListForm.initiateTask}" id="cl1"
                                useWindow="true"
                                windowModalityType="applicationModal">
              <af:setPropertyListener type="action" from="#{process}"
                                      to="#{viewScope.initiableTaskListForm.process}"/>
            </af:commandLink>
          </af:forEach>
        </af:panelList>
      </af:panelBox>
    </jsp:root>
    

    Sample Application

    You can get the sample application from this link. The sample application needs to be deployed on a SOA server. On a subsequent post I will demonstrate how to run this sample on the integrated weblogic server.
    In the sample, the InitiableTasksProject is deployed as an ADF library so it can easily be reused in other projects/ applications.

    Cheers!

    Sunday, July 17, 2011

    BPM 11g Java API Samples

    Please click on the link below to access the sample application that contains the runnable classes related to my posts on BPM 11g Java API. This link will be updated to reflect latest additions to the samples.
    BPM 11g Java API samples
    Cheers!

    Getting BPM InitiableTasks and Initiating a Task

    This is my third post on the series of post related to the Oracle BPM 11g Java API.
    The class below demonstrate how to get the BPM initiable tasks and how to initiate a task. It also contains a testGetProcessMetadata() method which could be useful to initiate a predefined task.
    package soadev.bpmclient;
    
    import java.util.List;
    
    import oracle.bpel.services.bpm.common.IBPMContext;
    
    import oracle.bpel.services.workflow.task.impl.TaskUtil;
    import oracle.bpel.services.workflow.task.model.Task;
    
    import oracle.bpm.services.instancemanagement.IInstanceManagementService;
    import oracle.bpm.services.processmetadata.IProcessMetadataService;
    import oracle.bpm.services.processmetadata.ProcessMetadataSummary;
    
    public class GetInitiableTasks {
        public static void main(String[] args) {
            GetInitiableTasks client = new GetInitiableTasks();
            client.testGetInitiatiableTasks();
            client.testInitiateTask();
            
        }
        public void testGetInitiatiableTasks() {
            System.out.println(">>> getting initiable tasks");
            try {
                IProcessMetadataService service =
                    Fixture.getBPMServiceClient().getProcessMetadataService();
                IBPMContext bpmContext = 
                    Fixture.getIBPMContext("pino", "password1");
                List<ProcessMetadataSummary> initiableTasks =  
                    service.getInitiatableProcesses(bpmContext);
                for(ProcessMetadataSummary pms: initiableTasks){
                    System.out.println(pms.getProjectName()+ "/" + pms.getProcessName());
                }
                
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        public void testGetProcessMetadata(){
            try {
                String processName = "HelloWorldProcess";
                String compositeDN = "default/HelloWorldProject!1.0*soa_a164d265-e961-4afb-b44b-50ea95a06fa1";
                IBPMContext ctx = Fixture.getIBPMContext("pino", "password1");
                IProcessMetadataService service =
                    Fixture.getBPMServiceClient().getProcessMetadataService();
                ProcessMetadataSummary pms = 
                    service.getProcessMetadataSummary(ctx, compositeDN, processName);
                System.out.println("compositeDN: " + pms.getCompositeDN());
                System.out.println("processName: " + pms.getProcessName());
                System.out.println("compositeName: " + pms.getCompositeName());
            } catch (Exception e) {
                // TODO: Add catch code
                e.printStackTrace();
            } 
        }
        public void testInitiateTask(){
            System.out.println(">>> initiating a task");
            try {
                IProcessMetadataService service =
                    Fixture.getBPMServiceClient().getProcessMetadataService();
                IBPMContext bpmContext = 
                    Fixture.getIBPMContext("pino", "password1");
                List<ProcessMetadataSummary> initiableTasks =
                    service.getInitiatableProcesses(bpmContext);
                //get the first initable task
                ProcessMetadataSummary pms = initiableTasks.get(0);
                IInstanceManagementService ims =
                    Fixture.getBPMServiceClient().getInstanceManagementService();
                Task task = ims.createProcessInstanceTask(bpmContext, 
                                        pms.getCompositeDN()+"/"+pms.getProcessName());
                System.out.println(">>> task initiated");
                printTask(task);
     
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        private void printTask(Task task){
            System.out.println(TaskUtil.getInstance().toString(task));
        }
    
    }
    

    Run output...
    C:\Oracle\11g\jdk160_24\bin\javaw.exe -client -classpath ...
    >>> getting initiable tasks
    EmployeeTransfersProject/EmployeeTransferProcess
    HelloWorldProject/HelloWorldProcess
    JobMasterProject/JobMasterCreationProcess
    >>> initiating a task
    >>> task initiated
    <task xmlns="http://xmlns.oracle.com/bpel/workflow/task">
       <title>Request Employee Transfer</title>
       <creator>pino</creator>
       <ownerRole>EmployeeTransfersProject.ProcessOwner</ownerRole>
       <priority>3</priority>
       <processInfo/>
       <systemAttributes>
          <assignedDate>2011-07-17T00:33:15+03:00</assignedDate>
          <createdDate>2011-07-17T00:33:15+03:00</createdDate>
          <digitalSignatureRequired>false</digitalSignatureRequired>
          <displayInfo>
             <applicationName>worklist</applicationName>
             <hostname>hoitpino.al-naghi.com</hostname>
             <httpPort>8001</httpPort>
             <httpsPort>0</httpsPort>
             <uri>/workflow/HumanTaskForms/faces/adf.task-flow?_id=RequestEmployeeTransfer_TaskFlow&amp;_document=WEB-INF/RequestEmployeeTransfer_TaskFlow.xml</uri>
          </displayInfo>
          <fromUser>
             <type>user</type>
          </fromUser>
          <numberOfTimesModified>1</numberOfTimesModified>
          <state>ASSIGNED</state>
          <systemActions>
             <action>VIEW_TASK</action>
             <displayName>VIEW_TASK</displayName>
          </systemActions>
          <taskId>04d4a518-f444-4e04-8a70-b64c58e79b0a</taskId>
          <taskNumber>200381</taskNumber>
          <updatedBy>
             <id>pino</id>
             <type>user</type>
          </updatedBy>
          <updatedDate>2011-07-17T00:33:15+03:00</updatedDate>
          <version>1</version>
          <taskDefinitionId>default/EmployeeTransfersProject!1.0*soa_a8c4f842-6b05-406a-be83-4ceb27588928/RequestEmployeeTransfer</taskDefinitionId>
          <workflowPattern>Participant</workflowPattern>
          <participantName>default.DefaultPerformer</participantName>
          <assignees>
             <id>EmployeeTransfersProject.Requester</id>
             <displayName></displayName>
             <type>application_role</type>
          </assignees>
          <isPartialTask>true</isPartialTask>
       </systemAttributes>
       <systemMessageAttributes/>
       <callback/>
       <category>HR</category>
       <sca/>
       <applicationContext>OracleBPMProcessRolesApp</applicationContext>
       <taskDefinitionId>default/EmployeeTransfersProject!1.0*soa_a8c4f842-6b05-406a-be83-4ceb27588928/RequestEmployeeTransfer</taskDefinitionId>
       <mdsLabel>soa_a8c4f842-6b05-406a-be83-4ceb27588928</mdsLabel>
       <creatorDisplayName>pino</creatorDisplayName>
       <customAttributes/>
    </task>
    Process exited with exit code 0.
    

    Please refer to the earlier post with regards to the Fixture class and the necessary BPM jars. You can access the sample application on this link.


    Cheers!

    Thursday, July 14, 2011

    Querying a BPM Process Instance and Adding Comments

    This is my second post on the series of post related to the Oracle BPM 11g Java API.
    The code below demonstrate how to query a specific BPM process instance and how to add comments to an instance:
    
    package soadev.bpmclient;
    
    import java.util.Calendar;
    import java.util.List;
    
    import oracle.bpel.services.bpm.common.IBPMContext;
    
    import oracle.bpm.client.common.BPMServiceClientException;
    import oracle.bpm.services.common.exception.BPMException;
    import oracle.bpm.services.instancemanagement.IInstanceManagementService;
    import oracle.bpm.services.instancemanagement.model.IIdentityType;
    import oracle.bpm.services.instancemanagement.model.IProcessComment;
    import oracle.bpm.services.instancemanagement.model.IProcessInstance;
    import oracle.bpm.services.instancemanagement.model.impl.IdentityType;
    import oracle.bpm.services.instancemanagement.model.impl.ProcessComment;
    import oracle.bpm.services.instancequery.IInstanceQueryService;
    
    public class GetProcessInstance {
        public static void main(String[] args) {
            GetProcessInstance client = new GetProcessInstance();
            client.testGetProcessInstance();
            client.testAddAttachment();
        }
    
        public void testGetProcessInstance() {
            try {
                String sampleProcessId = "150001"; //replace this with a valid id
                IBPMContext ctx = Fixture.getIBPMContext("pino", "password1");
                IInstanceQueryService queryService =
                    Fixture.getBPMServiceClient().getInstanceQueryService();
                System.out.println("retrieve sample instance");
                IProcessInstance instance =
                    queryService.getProcessInstance(ctx, sampleProcessId);
    
                printProcessInstance(instance);
    
            } catch (Exception e) {
                // TODO: Add catch code
                e.printStackTrace();
            }
        }
    
        private void printProcessInstance(IProcessInstance instance) {
            System.out.println(instance.getSystemAttributes().getProcessInstanceId());
            System.out.println(instance.getSystemAttributes().getProcessNumber());
            System.out.println(instance.getSystemAttributes().getState());
            System.out.println(instance.getProcessDN());
            System.out.println(instance.getCreator());
            System.out.println(instance.getSystemAttributes().getCreatedDate().getTime());
    
        }
        
        public void testAddAttachment(){
            try {
                String sampleProcessId = "150001"; //replace this with a valid id
                IBPMContext ctx = Fixture.getIBPMContext("pino", "password1");
                IInstanceQueryService queryService =
                    Fixture.getBPMServiceClient().getInstanceQueryService();
                IProcessInstance instance =
                    queryService.getProcessInstance(ctx, sampleProcessId);
                IInstanceManagementService service =
                    Fixture.getBPMServiceClient().getInstanceManagementService();
                IProcessComment comment = new ProcessComment();
                IIdentityType identity = new IdentityType();
                identity.setDisplayName(ctx.getUser());
                identity.setId(ctx.getUser());
                identity.setType("user");
                comment.setUpdatedBy(identity);
                comment.setComment("my programmatic comment3");
                comment.setUpdatedDate(Calendar.getInstance());
                service.addComment(ctx, instance, comment);
                //check if persisted
                IProcessInstance updatedInstance =
                    queryService.getProcessInstance(ctx, sampleProcessId);
                List<IProcessComment> comments = updatedInstance.getUserComment();
                for (IProcessComment c : comments) {
                    System.out.println(c.getComment());// the new comment should appear
                }
            } catch (BPMServiceClientException bpmsce) {
                // TODO: Add catch code
                bpmsce.printStackTrace();
            } catch (BPMException bpme) {
                // TODO: Add catch code
                bpme.printStackTrace();
            } catch (Exception e) {
                // TODO: Add catch code
                e.printStackTrace();
            }
        }
    }
    
    Please refer to the earlier post with regards to the Fixture class and the necessary BPM jars. Or click this link to access the sample application.

    Cheers!

    Querying Oracle BPM Process Instances

    This is the first in a series of posts related to the BPM 11g Java API. As far as I know, as of this writing, there is no official Oracle BPM 11g Java API documentation that is being release yet, but the corresponding jars are already present on a standard JDeveloper installation (probably after installing BPM Extension).
    The purpose of this post is to show simple runnable classes so readers can easily have an acquintance with the BPM 11g APIs, without needing to setup a complex web project.

    We can manipulate BPM processes through a BPMServiceClient that we can acquire from a BPMServiceClientFactory. Below is a utility class that I set to easily acquire a client instance for my testing. This class need the following jars in the classpath: Oracle.bpm.client.jar, Oracle.bpm.project.model.jar, and Oracle.bpm.runtime.jar which are present inside the BPM folders in "MIDDLEWARE_HOME\jdeveloper\soa\modules" directory.
    package soadev.bpmclient;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import oracle.bpel.services.bpm.common.IBPMContext;
    import oracle.bpel.services.workflow.client.IWorkflowServiceClient;
    import oracle.bpel.services.workflow.client.IWorkflowServiceClientConstants;
    import oracle.bpel.services.workflow.client.WorkflowServiceClientFactory;
    
    import oracle.bpm.client.BPMServiceClientFactory;
    import oracle.bpm.services.client.IBPMServiceClient;
    
    public class Fixture {
        private static String url = "t3://localhost:8001";
        
        public static BPMServiceClientFactory getBPMServiceClientFactory() {
         Map<IWorkflowServiceClientConstants.CONNECTION_PROPERTY, String> properties =
                new HashMap<IWorkflowServiceClientConstants.CONNECTION_PROPERTY, String>();
    
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY.CLIENT_TYPE,
                                 WorkflowServiceClientFactory.REMOTE_CLIENT);
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY
                                .EJB_PROVIDER_URL,url);
            properties.put(IWorkflowServiceClientConstants.CONNECTION_PROPERTY
                                          .EJB_INITIAL_CONTEXT_FACTORY,
                           "weblogic.jndi.WLInitialContextFactory");
            return BPMServiceClientFactory.getInstance(properties, null, null);
        }
    
        public static IBPMContext getIBPMContext(String username,
                                                 String password) throws Exception{      
            return getBPMServiceClientFactory().getBPMUserAuthenticationService()
                                          .authenticate(username, 
                                                        password.toCharArray(),
                                                        null);
        }
    
        public static IWorkflowServiceClient getIWorkflowServiceClient() {
            return getBPMServiceClientFactory().getWorkflowServiceClient();
        }
        
        public static IBPMServiceClient getBPMServiceClient(){
            return getBPMServiceClientFactory().getBPMServiceClient();
        }
    }
    


    Below is a sample class that demonstrate querying of BPM process instances. It shows how to compose Predicate, Ordering, and IInstanceQueryInput objects.
    package soadev.bpmclient;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import oracle.bpel.services.bpm.common.IBPMContext;
    import oracle.bpel.services.workflow.repos.Column;
    import oracle.bpel.services.workflow.repos.Ordering;
    
    import oracle.bpel.services.workflow.repos.Predicate;
    import oracle.bpm.services.instancemanagement.model.IProcessInstance;
    import oracle.bpm.services.instancequery.IColumnConstants;
    import oracle.bpm.services.instancequery.IInstanceQueryInput;
    import oracle.bpm.services.instancequery.IInstanceQueryService;
    import oracle.bpm.services.instancequery.impl.InstanceQueryInput;
    
    public class GetProcessInstances {
        
        public static void main(String[] args) {
            GetProcessInstances client = new GetProcessInstances();
            client.testGetProcessInstances();
        }
        
        public void testGetProcessInstances(){
            try {
                IInstanceQueryService queryService =
                    Fixture.getBPMServiceClient().getInstanceQueryService();
                IBPMContext bpmContext = 
                    Fixture.getIBPMContext("pino", "password1");
                List<Column> columns = new ArrayList<Column>();
                columns.add(IColumnConstants.PROCESS_ID_COLUMN);
                columns.add(IColumnConstants.PROCESS_NUMBER_COLUMN);
                columns.add(IColumnConstants.PROCESS_STATE_COLUMN);
                columns.add(IColumnConstants.PROCESS_TITLE_COLUMN);
                columns.add(IColumnConstants.PROCESS_CREATOR_COLUMN);
                columns.add(IColumnConstants.PROCESS_CREATEDDATE_COLUMN);
    
                Ordering ordering = new Ordering(IColumnConstants.PROCESS_NUMBER_COLUMN,
                                                 true,true);  
                Predicate pred = new Predicate(IColumnConstants.PROCESS_STATE_COLUMN,
                       Predicate.OP_EQ,
                       "OPEN");
                IInstanceQueryInput input = new InstanceQueryInput();
                input.setAssignmentFilter(IInstanceQueryInput.AssignmentFilter.MY_AND_GROUP);
                
                List<IProcessInstance> processInstances =
                    queryService.queryInstances(bpmContext, columns, pred, ordering,
                                                       input);
                System.out.println("ProcessId\tProcess#\tState\tTitle\t\t\t\t\tCreator\tCreadedDate");
                for (IProcessInstance instance : processInstances) {
                    System.out.println(instance.getSystemAttributes().getProcessInstanceId()
                                       + "\t" + instance.getSystemAttributes()
                                                                  .getProcessNumber() 
                                       + "\t" + instance.getSystemAttributes().getState() 
                                       + "\t" + instance.getTitle()
                                       + "\t" + instance.getCreator()
                                       + "\t" + instance.getSystemAttributes()
                                                            .getCreatedDate().getTime());
                }
                if (processInstances.isEmpty()){
                    System.out.println("no result");
                }
            } catch (Exception e) {
                // TODO: Add catch code
                e.printStackTrace();
            }
        }
    }
    

    Below is a sample result when I run it on my machine:

    You can access the sample application on this link.

    Kudus

    Kudus to the following blogs and OTN forum posts:

    Cheers!

    Tuesday, May 17, 2011

    ADF Faces Layout Tips: Stretching an af:region inside af:dialog/popup component

    The original solution to this post has been deprecated. Please see at bottom the proper solution.

    I have a hard time stretching the height of an af:region inside a pop-up dialog, so to make the life of others easy, below is a sample to show what I have figured out.

    <af:popup id="p1">
              <af:dialog id="d2" type="ok" resize="on" contentWidth="600"
                         contentHeight="400">
                <af:panelStretchLayout id="psl1"
                                       inlineStyle="height:inherit;"
                                       styleClass="AFStretchWidth">
                  <f:facet name="center">
                    <af:region value="#{bindings.sampletaskflow1.regionModel}"
                               id="r1"/>
                  </f:facet>
                </af:panelStretchLayout>
              </af:dialog>
            </af:popup>
    
    The trick is setting the 'inlineStyle' of the surrounding panelStretchLayout to 'height:inherit;'.

    Cheers,
    pino

    Important update to this post:

    To stretch a component inside a pop-up dialog you just need to set the ADF dialog's "stretchChildren" property to "first" as described by the following example:
      <af:popup id="p2">
          <af:dialog id="d3" type="ok"
                     resize="on" contentWidth="800" contentHeight="500"
                     stretchChildren="first">
               <af:region value="#{bindings.sampletaskflow1.regionModel}"
                          id="r3"/>
          </af:dialog>
      </af:popup>
    


    Saturday, February 26, 2011

    Bug: Boolean Accessors Prefixed with "is" not recognized as attribute in JDeveloper 11.1.1.4

    The biggest fail that I noticed in JDeveloper 11.1.1.4.0 (aka Patch Set 3) is the inability to recognized boolean properties that are prefixed with "is" like isActive() as property in the entity xml files. My disappointment is that- this is properly recognized in earlier releases. We wanted to jive with the most recent release, so we have a lot of refactoring to make.
    Below is a sample entity class and the corresponding entity xml file that was generated by JDev:
    package model;
    
    public class Employee {
        private Long id;
        private Boolean active;
        private String firstName;
        private String lastName;
    
        public void setActive(Boolean active) {
            this.active = active;
        }
    
        public Boolean isActive() {
            return active;
        }
    
        public void setId(Long id) {
            this.id = id;
        }
    
        public Long getId() {
            return id;
        }
    
        public void setFirstName(String firstName) {
            this.firstName = firstName;
        }
    
        public String getFirstName() {
            return firstName;
        }
    
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
    
        public String getLastName() {
            return lastName;
        }
    }
    
    Generated xml:
    <?xml version="1.0" encoding="UTF-8" ?>
    <JavaBean xmlns="http://xmlns.oracle.com/adfm/beanmodel" version="11.1.1.59.23"
              id="Employee" Package="model" BeanClass="model.Employee"
              isJavaBased="true">
      <Attribute Name="id" Type="java.lang.Long"/>
      <Attribute Name="firstName" Type="java.lang.String"/>
      <Attribute Name="lastName" Type="java.lang.String"/>
      <MethodAccessor IsCollection="false" Type="void" id="setActive"
                      ReturnNodeName="Return">
        <ParameterInfo id="active" Type="java.lang.Boolean" isStructured="false"/>
      </MethodAccessor>
      <MethodAccessor IsCollection="false" Type="java.lang.Boolean" id="isActive"
                      ReturnNodeName="Return"/>
      <ConstructorMethod IsCollection="true" Type="model.Employee"
                         BeanClass="model.Employee" id="Employee"/>
    </JavaBean>
    
    Notice that isActive() is not part of the attributes.

    Sunday, January 16, 2011

    ADF Faces RC: Right Aligning Components Inside ADF Column Footer Facet

    The image below shows a right aligned OutpuText component inside the ADF Column footer facet:

    To achieve the above alignment, simply enclosed the component inside a panelGrid as follows:
         <f:facet name="footer">
           <h:panelGrid columns="1" id="pg1" style="text-align:right;" width="100%">
             <af:outputText value="#{yourvaluebindinghere}"
                   id="ot8">
               <af:convertNumber type="currency" currencySymbol=" "/>
             </af:outputText>
           </h:panelGrid>
         </f:facet>
    

    Cheers!