Pages

Sunday, May 3, 2009

OSB Rest service with xml, json output

Inspired by an article RESTify your world..of Emiliano Pecis, I decided to make my own OSB Rest service which can return XML and JSON. To make this work I will use the XMLdb oradb servlet of the Oracle database. T o transform the oradb xml output to the JSON format I used the JSON-LIB libraries.
Here some pictures of the OSB rest service.
Request for order 1000 with XML as outputThe same, with now JSON as output.Request for all the employees with xml as output
Before we start we need to setup de Oracle Database.
Login with sqlplus as xdb. Now we can set the http port of XMLdb.

exec dbms_xdb.sethttpport(8080);

Give your Oracle schema's rights to the oradb servlet. For example the user HR.

grant xdb_webservices to hr;
grant xdb_webservices_over_http to hr;

We can test this by using this url http://localhost:8080/oradb/HR/EMPLOYEES , you will need to authenticate so use the HR account for this. The output would look this.


We can also get one employee by using this url. http://localhost:8080/oradb/HR/EMPLOYEES/ROW[EMPLOYEE_ID=198]
Now we are ready to start with the OSB. First create the Business services where we call the XMLdb servlets. Select any xml as input and http as transport. We need also to create a Service Account with the hr username / password and add this to the Business service.
The next step is to create the OSB Proxy Service. Select messaging as service type and as use text as response messaging type( because we want to return something, else you will get some errors). The transport is http with as endpoint url "/rest". In the message flow we add a Routnode with some If-then.

because the first thing we need to know which type of data is requested. For example employees or orders. In the second If-then we need to know if all the employees are requested or just one. If all is requested then we can call the HR Business Service. If not then we need to retrieve the requested Id and rewrite the endpoint url of the HR Business service. You can achieve this with a routing options component.

This is not enough the new endpoint url has to processed else it won't work. (escape-uri($url/text(), false()) )

The incoming part is ready, we can focus now on the output. Here we have to create a Pipeline Pair.
In the request parameter we need to retrieve the request url and the parameters. To get the url we can use. $inbound/ctx:transport/ctx:request/http:relative-URI/text() and the get the requested output we can use $inbound/ctx:transport/ctx:request/http:query-string/text() . These parameters will be used by the java callout. In the response pipeline I will add a java callout.

This java code transform the XML to JSON. To succesfull use a java methoud in OSB, it need to be a static method and because I need to process the $body variable, I must have a org.apache.xmlbeans.XmlObject method argument.
The JSON output can return output with datatypes, This datatype information isn't provided in the xml output of XMLdb so I made a convertor class for employees and orders. To make this work I use java reflection.
here is a fragment of the code.

private static void invoke(Object inst,Method m, JSONObject obj){
try {
m.invoke(inst, new Object[] { obj });
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}


private static JSONArray convert(JSONArray json,String type) {
ListIterator list = json.listIterator();

Class dynamicConvertorClass = null;
Object clazzInst =null;
Method m = null;

try {
dynamicConvertorClass = Class.forName("nl.whitehorses.json.convertor."+type.substring(0, 1).toUpperCase() + type.substring(1).toLowerCase());
clazzInst = dynamicConvertorClass.newInstance();
m = dynamicConvertorClass.getMethod("changeType", new Class[] { JSONObject.class });
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
while ( list.hasNext() ) {
JSONObject obj = (JSONObject)list.next();
invoke (clazzInst,m,obj);
}
return json;
}


private static JSONObject convert(JSONObject json,String type) {

Class dynamicConvertorClass = null;
Object clazzInst =null;
Method m = null;
try {
dynamicConvertorClass = Class.forName("nl.whitehorses.json.convertor."+type.substring(0, 1).toUpperCase() + type.substring(1).toLowerCase());
clazzInst = dynamicConvertorClass.newInstance();
m = dynamicConvertorClass.getMethod("changeType", new Class[] { JSONObject.class });
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
invoke (clazzInst,m,json);

return json;
}


public static String xmlToJson(XmlObject xml, String type, String output)
{
if ( type.indexOf("/") > 0 ) {
type = type.substring(0,type.indexOf("/"));
}

System.out.println("type "+type);
System.out.println("content "+xml.xmlText());

if ( output.equalsIgnoreCase("output=json") ){

if ( !type.equalsIgnoreCase("not")) {
XMLSerializer ser = new XMLSerializer();
Object res = ser.read( xml.xmlText() );
if ( res instanceof JSONArray ) {
JSONArray json = (JSONArray) res;
json = convert(json,type);
return json.toString(1,0);
}
if ( res instanceof JSONObject ) {
JSONObject json = (JSONObject) res;
json = convert(json,type);
return json.toString(1,0);
}
}
}
return xml.xmlText();
}

The convertor of the employees request looks like this.

public void changeType(JSONObject obj){

Object a = obj.get("SALARY");
if ( a!= null) {
obj.put("SALARY",new Float(a.toString()));
}
a = obj.get("EMPLOYEE_ID");
if ( a!= null) {
obj.put("EMPLOYEE_ID",new Float(a.toString()));
}
a = obj.get("MANAGER_ID");
if ( a!= null) {
obj.put("MANAGER_ID",new Float(a.toString()));
}
a = obj.get("DEPARTMENT_ID");
if ( a!= null) {
obj.put("DEPARTMENT_ID",new Float(a.toString()));
}
a = obj.get("HIRE_DATE");

SimpleDateFormat sdfInput = new SimpleDateFormat ("d-MMM-yy") ;
Date date = null;
try {
date = sdfInput.parse(a.toString());
} catch (ParseException e) {
e.printStackTrace();
}
if ( a!= null) {
obj.put("HIRE_DATE",date);
}

In JDeveloper 11g I made a jar deployment and add this jar to the OSB project. Together with the required jars. Our json jar need a reference to the json-lib and the json-lib need a reference to the other jars.
That's all just deploy this to OSB server.
Here is my OSB export jar and the JDeveloper java callout project.

11 comments:

  1. Edwin,

    Is there an Ant task or Maven mojo to generate an OSB configuration jar from the source, or is it only possible generate this jar from the sbconsole and workshop? I'd really like to be able to automate this process...

    Thanks

    ReplyDelete
  2. Hi,

    I spoke with Eric Elzinga and he came up with this link

    http://kr.forums.oracle.com/forums/thread.jspa?threadID=943099&tstart=0

    hope this helps

    thanks Edwin

    ReplyDelete
  3. http://download.oracle.com/docs/cd/E13159_01/osb/docs10gr3/eclipsehelp/tasks.html
    "Exporting a Configuration Using Ant "

    That one should do it.

    ReplyDelete
  4. Thanks for the responses guys! I'm a bit surprised that oracle is going to make me install eclipse on my build server but OK :P

    Thanks!

    ReplyDelete
  5. Hi Biemond,

    We designed one REST based OSB service calling an SOAP service . We need to convert the SOAP response to a JSON response and remove the SOAP namespaces . We just want to keep the key value pairs in JSON response .

    We successfully got the complete JSON response but we are not able to remove the soap namespaces.

    Can you help us in this regard to remove SOAP namespaces from JSON response ?

    ( The jar and project which we used to convert the complete SOAP response to JSON is present at the following url with name osb-206-JSONREST:

    https://java.net/projects/oraclesoasuite11g/pages/OSB )

    Regards,
    Neeraj

    ReplyDelete
    Replies
    1. Hi,

      What I do mostly do is to create a Java JSON Restful service based on Jersey, own entities, add a java ws proxy, This way I am totally in control else I would wait for OSB 12c ( coming in a few months )

      Thanks

      Delete
    2. What's new in OSB 12c in sense of JSON? Any hope for built-in conversion to/from XML for passing down to XQueries?

      Delete
    3. Hi Neeraj,

      I am stuck at the same point. Can you please share what steps you had followed to overcome the issue, if it was resolved?

      Regards,
      Nameet

      Delete
  6. Hi Edwin,

    Nice post. I have a question. If i want to invoke a workday rest api and the rest api insert the data into workday custom objects.

    My question is the workday api needs the data in json and when i invoke in bpel i am passing as xml. how to convert this xml to json when i invoke the rest api.

    Thanks
    Safeeq

    ReplyDelete
  7. Hi dear Edwin,

    Do you have an example/Tutorial of Oracle OSB calling an external Http Servlet?

    Regards!!

    ReplyDelete
  8. Hi Edwin,

    Can you please share the OSB code that you have created in this blog post.

    Regards,
    Nameet

    ReplyDelete