Friday 29 April 2011

How to Undeploy Composite Manually

Sometime I noticed that you can’t undeploy the composite from SOA EM console.It is little bit irritating and can be of various reason like not deployed properly , database reference is null etc.

But I figured out undeployment procedure of SOA composite manually apart from wizard based.It worked fine for me.Here are the steps,

Login to EM console and go to mds configuration,

image

Export metadata,

image

soa-infra_metadata.zip file will be downloaded.Unzip the same and you will find deployed-composites folder and underneath deployed-composites.xml like below,

image

Delete the entry of whatever the composite you want to undeploy.You can delete the respective composite from your partition folder. Then zip it again and import the MDS from EM.

You need to restart the server and after that your composite will be gone !!!

Monday 4 April 2011

Change DB JNDI dynamically in SOA 11g

Lets take one simple example , we want to retrieve data from employees table of HR schema based on employee_id.For that I created a datasource and outbound connection factory in database adapter.I passed on the newly created JNDI name in adapter .jca file accordingly and deployed.It ran successfully.

But my requirement is to pass on the JNDI name dynamically from descriptor, means if the destination database change then user can change the JNDI name from soa preference of deployed process, no need for redeployment. Here are some simple steps to achieve that,

Say like below you created your composite,

image

In composite.xml for the BPEL process I created one preference say,jndiVar like,

image

Here is my BPEL flow outline,

image

At setJNDI I’m assigning preference value to a process variable,

image

Then after creating the adapter go to the BPEL source and add

<bpelx:inputProperty name="jca.jndi" variable="jndiVar"/> as below,

image

Then deploy the process and change your preference value accordingly to point to right JNDI as below,

Farm_soa_domain > Weblogic Domain > soa_domain > right mouseclick and select ‘System MBean Browser’.

image

Navigate to Application Defined MBeans > oracle.soa.config > Server : soa_server1 > SCAComposite > your_project > SCAComposite.SCAComponent > your bpel_process.
Select the Attribute ‘Properties’.

image

Change the value of our preference,set JNDI accordingly and click apply.

image

Thats all your adapter will retrieve the data using new JNDI, obviously that JNDI should be defined in weblogic.

Sunday 3 April 2011

Language Translator as XPATH query

In this blog I’ll explain how to use Google language translator API in custom XPATH function for interaction with any database with different language apart from English.

Before starting you need to download two jar file, Json_simple-1.1.jar and Google-api-translate-java.jar.

Create a java file CustomExtensions like below,


package com.shrik.ws.xslt;


import com.google.api.translate.Language;
import com.google.api.translate.Translate;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;

import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;

import org.json.simple.JSONObject;
import org.json.simple.JSONValue;

public class CustomExtensions {
    // sample method

    public static String helloCustomXSLT(String value) {

        return "Hello " + value;
    }
    //language translator

    public static String googleTranslationService =
        "http://ajax.googleapis.com/ajax/services/language/translate";


  

    public static String translateString(String sourceString,
                                         String sourceLanguage,
                                         String targetLanguage) {
        HttpURLConnection connection = null;
        OutputStreamWriter wr = null;
        BufferedReader rd = null;
        StringBuilder sb = null;
        String line = null;

        URL serverAddress = null;

        try {
            serverAddress =
                    new URL(googleTranslationService + "?v=1.0&&q=" + sourceString.replace(' ',
                                                                                           '+') +
                            "&&langpair=" + sourceLanguage + "%7C" +
                            targetLanguage);
            //set up out communications stuff
            connection = null;

            //Set up the initial connection
            connection = (HttpURLConnection)serverAddress.openConnection();
            connection.setRequestMethod("GET");

            connection.setDoOutput(true);
            connection.setReadTimeout(10000);

            connection.connect();

            //get the output stream writer and write the output to the server
            //not needed in this example
            //wr = new OutputStreamWriter(connection.getOutputStream());
            //wr.write("");
            //wr.flush();

            //read the result from the server
            rd =new BufferedReader(new InputStreamReader(connection.getInputStream()));
            sb = new StringBuilder();

            while ((line = rd.readLine()) != null) {
                sb.append(line + '\n');
            }

            return (extractTranslationFromJSON(sb.toString()));

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (ProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //close the connection, set all objects to null
            connection.disconnect();
            rd = null;
            sb = null;
            wr = null;
            connection = null;
        }
        return null;
    }
  public static String extractTranslationFromJSON(String response) {
      final JSONObject jsonObj = (JSONObject)JSONValue.parse(response);
      String translation = null;
      if (jsonObj != null && jsonObj.containsKey("responseData")) {
          final JSONObject responseData =
              (JSONObject)jsonObj.get("responseData");
          translation = responseData.get("translatedText").toString();
      }
      return translation;
  }


   /*
      public static String translate(String sourceString, String sourceLanguage,
                                   String targetLanguage) {
        return extractTranslationFromJSON(translateString(sourceString,
                                                          sourceLanguage,
                                                          targetLanguage));

    }

    public static String googleTranslateAPI(String sourceString, String sourceLanguage,
                                            String targetLanguage ) throws Exception {
                                              Translate.setHttpReferrer(googleTranslationService);
      
            return  Translate.execute("Hello World", Language.ENGLISH, Language.GERMAN);
       
    }
    */ 
}


Then unzip the above two jars and add it in your classes directory, for Json_simple-1.1.jar add root org package and for Google-api-translate-java.jar add google and tecnick under com package.Create a META-INF folder under classes and create ext-mapper-xpath-functions-config.xml file underneath as below,

<soa-xpath-functions version="11.1.1"
                     xmlns="http://xmlns.oracle.com/soa/config/xpath"
                     xmlns:customXSLT="http://www.oracle.com/XSL/Transform/java/com.shrik.ws.xslt.CustomExtensions">
 
 
  <function name="customXSLT:helloCustomXSLT">
      <className>com.shrik.ws.xslt.CustomExtensions</className>
      <return type="string"/>
      <params>
        <param name="value" type="string"/>
      </params>
      <desc/>
      <detail>
        <![CDATA[This function returns the value prefixed by a complex Hello statement.]]>
      </detail>
  </function>
 
  <function name="customXSLT:translateString">
      <className>com.shrik.ws.xslt.CustomExtensions</className>
      <return type="string"/>
      <params>
        <param name="value1" type="string"/>
        <param name="value2" type="string"/>
        <param name="value3" type="string"/>
      </params>
      <desc/>
      <detail>
        <![CDATA[This function returns the translated value . It takes 3 input parameters, source,sourceLn,destLn.]]>
      </detail>
  </function> 
 
</soa-xpath-functions>

Create a jar deployment profile in your Jdeveloper and filter classes as below (this give you also snapshot of class hierarchy)

image
Then deploy to jar file and add the same to soa under preference menu of Jdeveloper,

image

Restart the Jdeveloper and create a sample xslt file,under user defined function you will find yours,

image

Then use it by providing any word to translate ,source language and destination language,

image

Test the XSLT and you will get the desired output,

image

image

To make the XPATH available at runtime please follow the last section of http://shrikworld.blogspot.com/2011/03/errorhandling-in-soa-11g.html.

HTTP Adapter in SOA 11g

At SOA 10g I faced a lot of difficulty while invoking a REST endpoint having POST or GET method as binding verb.You have to create your own custom wsdl and embed your message type and binding etc..
But SOA 11g comes with HTTP adapter , through this blog I’ll demonstrate invocation of Yahoo Geolocation REST service using HTTP adapter.
You can get the endpoint details in this location http://developer.yahoo.com/maps/rest/V1/geocode.html. If you paste http://local.yahooapis.com/MapsService/V1/geocode?appid=YD-9G7bey8_JXxQP6rxl.fBFGgCdNjoDMACQA--&street=701+First+Ave&city=Sunnyvale&state=CA in browser then you will get below result.
<?xml version="1.0"?>
<ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:maps" 
xsi:schemaLocation="urn:yahoo:maps http://local.yahooapis.com/MapsService/V1/GeocodeResponse.xsd">
<Result precision="address">
<Latitude>37.416397</Latitude>
<Longitude>-122.025055</Longitude>
<Address>701 1st Ave</Address>
<City>Sunnyvale</City>
<State>CA</State>
<Zip>94089-1019</Zip>
<Country>US</Country>
</Result></ResultSet>
<!-- ws01.ydn.ac4.yahoo.com compressed/chunked Sun Apr  3 00:08:24 PDT 2011 –>
But we need to achieve this using adapter.This service has got its input and 
output schema structure like below,

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
  targetNamespace="urn:yahoo:maps"
  xmlns="urn:yahoo:maps"
  elementFormDefault="qualified">

    <xs:element name="ResultSet">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Result" type="ResultType" minOccurs="0" maxOccurs="50" />
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:complexType name="ResultType">
        <xs:sequence>
            <xs:element name="Latitude" type="xs:decimal" />
            <xs:element name="Longitude" type="xs:decimal" />
            <xs:element name="Address" type="xs:string" />
            <xs:element name="City" type="xs:string" />
            <xs:element name="State" type="xs:string" />
            <xs:element name="Zip" type="xs:string" />
            <xs:element name="Country" type="xs:string" />
        </xs:sequence>
        <xs:attribute name="precision" type="xs:string" />
        <xs:attribute name="warning" type="xs:string" use="optional"/>
    </xs:complexType>
    <xs:element name="RequestSet">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="appid" type="xs:string"/>
                <xs:element name="street" type="xs:string"/>
                <xs:element name="city" type="xs:string"/>
                <xs:element name="state" type="xs:string"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>
<!-- ws13.ydn.gq1.yahoo.com compressed Fri Oct 22 03:06:44 PDT 2010 –>

We’ll bind RequestSet to service input parameter and Result to service output.

Drag and drop an HTTP adapter in Reference lane ,

image

image

In HTTP Binding Configuration give the value as mentioned,

image

Bind input and output message type and click finish,

image

Then create a composite like below where adapter input parameter is same as process input and output parameter would be process output parameter,

image

Deploy the composite and test by passing proper value like below,

image

and in response you will get,

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing">
    <env:Header>
        <wsa:MessageID>urn:D6C31E005DDA11E0BFCB817CB1165FA2</wsa:MessageID>
        <wsa:ReplyTo>
            <wsa:Address>http://www.w3.org/2005/08/addressing/anonymous</wsa:Address>
        </wsa:ReplyTo>
    </env:Header>
    <env:Body>
        <ResultSet xmlns:ns0="urn:yahoo:maps" xmlns="urn:yahoo:maps">
            <ns0:Result precision="address">
                <ns0:Latitude>37.416397</ns0:Latitude>
                <ns0:Longitude>-122.025055</ns0:Longitude>
                <ns0:Address>701 1st Ave</ns0:Address>
                <ns0:City>Sunnyvale</ns0:City>
                <ns0:State>CA</ns0:State>
                <ns0:Zip>94089-1019</ns0:Zip>
                <ns0:Country>US</ns0:Country>
            </ns0:Result>
        </ResultSet>
    </env:Body>
</env:Envelope>


So Invoking REST endpoint from composite is just one click away !!!!!!!!

Saturday 2 April 2011

Error Handling in SOA 11g: Part 2

Please have a look at my previous post http://shrikworld.blogspot.com/2011/03/errorhandling-in-soa-11g.html before get started.There I have mentioned error queue in fault policy. The moto is that we can bind action ref="ora-errorQ" with below property set,

<Action id="ora-errorQ">
        <javaAction className="com.shrik.ErrorHospitalQueue"
                    defaultAction="ora-terminate"
                    propertySet="enqueue-properties">
          <returnValue value="REPLAY" ref="ora-terminate"/>
          <returnValue value="RETRHOW" ref="ora-rethrow-fault"/>
          <returnValue value="ABORT" ref="ora-terminate"/>
          <returnValue value="RETRY" ref="ora-retry"/>
          <returnValue value="MANUAL" ref="ora-human-intervention"/>
        </javaAction>
      </Action>

<propertySet name="enqueue-properties">
        <property name="aq.queueconnectionfactory">jms/shrikCF</property>
          <property name="aq.queue">jms/shrikQueue</property>
      </propertySet>

So first of all you need to create a jms queue and connection factory in weblogic server , in my case it is shrikQueue and shrikCF and change the enqueue-properties value accordingly.

Here is the code excerpts that help you to enqueue data into shrikQueue,

package com.shrik;


import com.collaxa.cube.engine.fp.BPELFaultRecoveryContextImpl;

import java.io.IOException;

import java.util.ArrayList;
import java.util.Map;
import java.util.UUID;

import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.QueueConnectionFactory;
import javax.jms.Session;
import javax.jms.TextMessage;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import oracle.integration.platform.faulthandling.recovery.RejectedMsgRecoveryContext;
import oracle.integration.platform.faultpolicy.IFaultRecoveryContext;
import oracle.integration.platform.faultpolicy.IFaultRecoveryJavaClass;

public class ErrorHospitalQueue implements IFaultRecoveryJavaClass {

    private String queueCF;
    private String queueName;

    public ErrorHospitalQueue() {
        super();
    }


    public String handleFault(IFaultRecoveryContext iFaultRecoveryContext) {
        BPELFaultRecoveryContextImpl ctx =
            (BPELFaultRecoveryContextImpl)iFaultRecoveryContext;
        Map properties = iFaultRecoveryContext.getProperties();
        UUID uuid = UUID.randomUUID();

        ctx.addAuditTrailEntry("Enqueueing Data into shrikQueue...");

        ctx.addAuditTrailEntry(createEventPayload(ctx));
        ctx.addAuditTrailEntry((String)getParameterValue((ArrayList)properties.get("aq.queueconnectionfactory")));
        ctx.addAuditTrailEntry((String)getParameterValue((ArrayList)properties.get("aq.queue")));
        try {
            enqueueAqEvent(createEventPayload(ctx), uuid, properties, ctx);
        } catch (JMSException e) {
            e.printStackTrace();
        } catch (NamingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "ora-terminate";
    }

    private String createEventPayload(IFaultRecoveryContext context) {

        String eventPayload =
            "<AdminFault xmlns=\"http://www.shrik.com/\">\n" +
            " <ecid>UNKNOWN_ECID</ecid>\n" +
            "</AdminFault>";

        if (context instanceof RejectedMsgRecoveryContext) {

            RejectedMsgRecoveryContext rejectedMessageContext =
                (RejectedMsgRecoveryContext)context;
            String ecid = null;
            if (rejectedMessageContext.getRejectedMessage() != null &&
                rejectedMessageContext.getRejectedMessage().getEcid() !=
                null) {
                ecid = rejectedMessageContext.getRejectedMessage().getEcid();
            } else if (rejectedMessageContext.getFault() != null &&
                       rejectedMessageContext.getFault().getECID() != null) {
                ecid = rejectedMessageContext.getFault().getECID();
            }
            eventPayload = eventPayload.replace("UNKNOWN_ECID", ecid);
        } else if (context instanceof BPELFaultRecoveryContextImpl) {
            BPELFaultRecoveryContextImpl bpelFaultRecoveryContextImpl =
                (BPELFaultRecoveryContextImpl)context;
            eventPayload =
                    eventPayload.replace("UNKNOWN_ECID", bpelFaultRecoveryContextImpl.getECID());

        }

        return eventPayload;
    }


    public void enqueueAqEvent(String input, UUID uuid, Map props,
                               BPELFaultRecoveryContextImpl ctx) throws JMSException,
                                                                        NamingException,
                                                                        IOException {

        Session session = null;
        MessageProducer publisher = null;
        TextMessage message = null;
        Context context = new InitialContext();
        QueueConnectionFactory connectionFactory =
            (QueueConnectionFactory)context.lookup((String)getParameterValue((ArrayList)props.get("aq.queueconnectionfactory")));
        Connection connection =
            (Connection)connectionFactory.createConnection();
        Queue errQueue =
            (Queue)context.lookup((String)getParameterValue((ArrayList)props.get("aq.queue")));
        session =
                (Session)connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

        publisher = session.createProducer(errQueue);

        message = session.createTextMessage(input);

        message.setJMSCorrelationID(uuid.toString());
        connection.start();
        publisher.send(message);
        connection.stop();
        connection.close();


    }

    private String getParameterValue(ArrayList parameterList) {
        String value = null;
        if (parameterList != null && parameterList.size() > 0)
            value = (String)parameterList.get(0);
        return value;
    }

    public void handleRetrySuccess(IFaultRecoveryContext iFaultRecoveryContext) {
        System.out.println("This is for retry success");
        handleFault(iFaultRecoveryContext);
    }

    public void setQueueCF(String queueCF) {
        this.queueCF = queueCF;
    }

    public String getQueueCF() {
        return queueCF;
    }

    public void setQueueName(String queueName) {
        this.queueName = queueName;
    }

    public String getQueueName() {
        return queueName;
    }
}

After updating the jar you will be able to see message with ecid got enqueued on error scenario, here is the sample message structure,

<AdminFault xmlns="http://www.shrik.com/">
<ecid>11d1def534ea1be0:-918321b:12f07773909:-8000-0000000000000c0d</ecid>
</AdminFault>

You can customize the message as per your need.