Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Thursday, May 30, 2013

The "cool" way to do base64 decode with OSB 11.1.1.6

Two years ago, i did a post on how to use proxy java callout to base64 decode with OSB 11.1.1.3 http://yuanmengblog.blogspot.com/2011/04/base64-decoder-for-osb.html.

Now i have 11.1.1.6. Obviously, oracle has moved those jar files around, so you will have to find out where the xerces jar file is if you want to do the same thing in 11.1.1.6.

Well, a colleague brought to my attention something I thought was very cool: using XSLT java call to do the base64 decode. Then you don't need to do the proxy java callout, therefore, saving the trouble of uploading some custom jar file. That sounded very cool, so i decided to give it a shot.

For that we need to resolve two issues. 1. what's the Oracle xslt processor's syntax for java call (it appears to me the XSLT java call syntax varies among different xslt processors). 2. What is the new base64 decoder class in 11.1.1.6. The xslt code below answers both of these questions:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:b64="http://www.oracle.com/XSL/Transform/java/weblogic.apache.xerces.impl.dv.util.Base64"
  >
  <xsl:template match="/">
  <foo>
        <xsl:value-of select="b64:decode('d2xzdXNlcjp3ZWxjb21lMQ==')"/>
    </foo>
  </xsl:template>
</xsl:stylesheet>

plug it into your proxy assignment activity, then you will get an output of
<foo xmlns:b64="http://www.oracle.com/XSL/Transform/java/weblogic.apache.xerces.impl.dv.util.Base64">wlsuser:welcome1</foo>

Keep in mind, directly referencing an undocumented/unsupported oracle class "weblogic.apache.xerces.impl.dv.util.Base64" is a tricky business, proceed at your own risk. currently, this class is inside "com.bea.core.apache_1.3.0.1.jar" under "C:\Oracle\Middleware\Oracle_OSB1\modules" for my 11.1.1.6 install. If oracle decides to change that, then you need to update your xslt as well.

testing on the command line (in my environment):
set classpath=C:\Oracle\Middleware\oracle_common\modules\oracle.xdk_11.1.0\xmlparserv2.jar;c:\Oracle\Middleware\Oracle_OSB1\modules\com.bea.core.apache_1.3.0.1.jar;

first jar is for oraxsl, 2nd jar is for base64 decoding:

java oracle.xml.parser.v2.oraxsl   input.xml     test.xslt

Wednesday, May 22, 2013

Java Web Service Resource Injection Fails on JMS Connection Factory When JMS Server Migrates

Here is the problem:
We have a clustered Weblogic environment. Let's say two managed servers M1 and M2. We have JMS server that needs be migrated among the managed servers. The JMS server is initially targeted to M1.

We have web services running on the cluster. One service publishes messages to the JMS server. This web service uses Java resource injection for the JMS connection factory like:
    @Resource(mappedName="ll.jmsprovider.XAConnectionFactory")
    private ConnectionFactory connectionFactory;

When the service is deployed to the cluster. "connectionFactory" is properly initialized, everything is fine. Even when we shutdown M1, the JMS resource is migrated to M2, the "connectionFactory" still works fine on M2. We can still publish messages.

The problem is when we bring back up M1, and shutdown M2. That's when "connectionFactory" doesn't work anymore. I suspect there might be some configurations on migration policy to make "connectionFactory" work seamlessly when the migration happens. But I don't know how. So my kludge is to recreate the "connectionFactory" on the fly. Here is the entire "hack":

package pubmsg;
import javax.annotation.Resource;
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.transaction.SystemException;

@WebService(targetNamespace = "http://pubmsg.org/")
public class msgSender {
    @Resource(mappedName="ll.jmsprovider.XAConnectionFactory")
    private ConnectionFactory connectionFactory;
    @Resource(mappedName="ll.jms.partner.enroll.queue")
    private javax.jms.Queue queue;
      
    public msgSender() {
        super();
    }

    @WebMethod
    @Oneway
    public void sendMessage(String msg) {
        Connection conn = null;
        try {
            try {
                 conn = connectionFactory.createConnection(); // my "hack"
            }
            catch (Exception e) {
                InitialContext ctx = new InitialContext();      
                connectionFactory = (ConnectionFactory) ctx.lookup("ll.jmsprovider.XAConnectionFactory");
                conn = connectionFactory.createConnection();
                queue = (Queue) ctx.lookup("ll.jms.partner.enroll.queue");
            }
   Session session = conn.createSession(false, Session.CLIENT_ACKNOWLEDGE);
   TextMessage message = session.createTextMessage(msg);
   MessageProducer messageProducer = session.createProducer(queue);
   messageProducer.send(message);
        }
        catch (NamingException e) {}
        catch (JMSException e) {}
        finally {
                if (conn!=null) {
                        try {
                                conn.close();
                        } catch (JMSException e) {}
                }
        }
    }
}

Not the most elegant solution. Oh, well, i need to move on...

Ironically, the purpose of this blog is keep track what i did and how i did it. But it has been several weeks since I worked on it, I already forgot the steps how I created the project :( So I just clicked through JDeveloper try to re-construct the same thing. I believe this is a sequence that can create a java based web service in Jdeveloper (11.1.1.6):

Part I - Ceate a "Web Project"
 1. select project name
 2. select default "serverlet 2.5..."
 3. chose "None" on the "page flow technology" screen
 4. chose no libraries.
 5. select doc root, app name, context root etc.
 6. finish

Part II - add a Java class,
    add the public methods as you need

Part III - add "Web Services->Java Web Service"
   select your java class, from that point on, pretty much follow the screen. That should just work.

====
hmm, i did find another way to create the Java web service: you can start with a generic application, then select "Web Services" for the default project, then "java" is selected by default. However, this process only works for the 1st default project. Once the application is created, and you try to add new project, and you pick "web services", you'll get different options, talking about confused...

Friday, May 10, 2013

Eclipse creates Java Web service client with WSS name token security

This is a combo task. It illustrates an end to end scenario of creating a Web service client based on a WSDL. Then it shows a way to add WSS name token security header, which ironically is different from the previous post http://yuanmengblog.blogspot.com/2013/05/jax-ws-client-call-web-services-with-ws.html

First of all, I use the same fooPS proxy service from OSB (from the previous post). You can find the wsdl from the previous post. I use Eclipse 3.7.1. Here are the steps:

Select JavaEE perspective. Create a new Web Service Client project: File->Other->Web Service Client.

On the next screen, enter your WSDL URL, then move the slide on the left side all the way up.

Then just click finish and let Eclipse does its magic.

When all said and done, Eclipse generates these files:
  • FooBPEL.java - interface, extends Remote
  • FooBPELBindingStub.java - implments FooBPEL and extends Stub
  • FooBPELProxy.java - implements FooBPEL
  • Foobpel_client_ep.java - interface implements Service
  • Foobpel_client_epLocator.java - extends Foobpel_client_ep
I added a server in Eclipse to point to my local Weblogic.


To deploy the new project to the server (I suppose there are better ways to do it): Select Run, Run As, Run on Server, then select the ear to deploy. I only do this once. If I make changes later, i just right click on the server (Tab on the bottom), then select "Publish".

When the deployment is finished, you will get a link http://localhost:7001/WebServiceProject/sampleFooBPELProxy/TestClient.jsp (again, all of these are auto generated, i haven't done a thing yet).

As you can see there is one "process()" method. Try invoke it, it would fail, because there is no security header. For sanity check, you can go to OSB fooPS, remove the WSS security policy, then try to invoke "process()" from the test page again, it should work correctly. Otherwise, you got other problems you need to sort out first before worrying about the security. If it works, then you just succesfully created a Web service client with Eclipse.

Next, I'll show you how I managed to add the security header to the client. In fact, the change is very mimium, but it took me a while to figure it out.

Firstly, i went down a path that did not work out. In the previous post (command line java web service client), the client program relies on casting the "port" into "BindingProvider", then set the security header that way. I just couldn't make it work with the Eclipse generated classes. I couldn't find the equivalent of the "Port" class in this case. Although there is getPort() method in "Foobpel_client_epLocator" class, but I have no way to cast it into the BindingProvider.

Here is what finally made it work: go to FooBPELProxy.java, find "process()" method, make changes as below:
...
import javax.xml.namespace.QName;
import javax.xml.ws.BindingProvider;
import weblogic.wsee.security.unt.ClientUNTCredentialProvider;
import weblogic.xml.crypto.wss.provider.CredentialProvider;
import weblogic.xml.crypto.wss.WSSecurityContext;
import org.apache.axis.message.SOAPHeaderElement;
import javax.xml.soap.SOAPElement;
...

 public java.lang.String process(java.lang.String input)
   throws java.rmi.RemoteException {
  if (fooBPEL == null)
   _initFooBPELProxy();

  org.apache.axis.client.Stub stub = null;
  try {
   stub = (org.apache.axis.client.Stub) fooBPEL;
   String wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
   String tstr = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText";
   SOAPHeaderElement security = new SOAPHeaderElement(wsse, "Security");
   SOAPElement usernameToken = security.addChildElement("UsernameToken");
   SOAPElement username = usernameToken.addChildElement("Username");
   username.addTextNode("weblogic");
   SOAPElement pwd = usernameToken.addChildElement("Password");
   pwd.addTextNode("welcome1");
   pwd.setAttribute("Type", tstr);
   stub.setHeader(security);
  } catch (Exception e) {
   System.out.println("exception=" + e.getMessage());
  }
  return fooBPEL.process(input);
 }

That's my hack to make it work. This may not be the way you code for production, it's just a proof of concept. 

JAX WS client call web services with WS-security name token - java command line version

I have an OSB proxy service running on http://localhost:8011/OWSM_Demo/fooPS?wsdl with WS name token security policy enabled. I need to call the service with the proper security header from Java.There are two scenarios: 1. call from command line Java app 2. call from Weblogic web app.

This post covers the command line verion. Web app version will be covered in the next post.

I followed this link: http://middlewaremagic.com/weblogic/?p=18. I first made the sample works as-is. I literrally followed the instructions with variations of my local environment. I have the SOA suite installed locally, i tested adminServer as well as the osb_server1 server. Here is what I did for osb_server1:
  1. In build.xml file, I just changed the weblogic/password, then changed following line for the server:<property name="wls.server.name" value="osb_server1"/> and http://localhost:8011/SecureHelloWorldImpl/SecureHelloWorldService?WSDL
  2. I ran setDomainEnv.cmd under C:\Oracle\Middleware\user_projects\domains\soa_domain\bin
  3. Then I just ran "ant" (the default target is "all").
The default target "all" compiles and deploys a sample web service to the weblogic server (AdminServer or osb_server1). Then generates the client stubs and finally compiles client code and runs the client.

The default build process first creates a web service with WSS name token security policy enabled. Then it generates the client classes based on the WSDL (very import point). Finally, it just invokes the service.This is all good.

Now I need to adapt the same sample to invoke my own OSB fooPS. Keep in mind, if you are only interested in creating a client that can call the OSB proxy fooPS, you only need do "ant client run" (skipping the server tasks).

If you read the build.xml file carefully, you'll see the "client" target is essentially independent of the actual sever implementation. It mostly generates the client classes based on the WSDL. From that I assume I can merely change the WSDL URL and make it work for my own OSB proxy service. Here is how I did it:

I changed WSDL URL in build.xml to point my service http://localhost1:8011/OWSM_Demo/fooPS?wsdl (see end of this post), then I ran "ant client" (I manully cleaned out the "webservicesSecurity_client" directory, you can use ant "clean" task to do it).

Of course, the build would fail because SecureHelloWorldClient.java can't find the original java classes from the original sample. But it did generate the client classes based on the new OSB WSDL, they are:
  • FooBPEL.java - interface
  • FooBPELAsyncHandler.java - I didn't do anything with this one
  • FoobpelClientEp.java - this one extends Service
  • ObjectFactory.java - didn't do anything
  • package-info.java - didn't do anything
  • Process.java - this is based on the only "operation" from the wsdl
  • ProcessResponse.java - operation response based on the WSDL
Now, change SecureHelloWorldClient.java from
  • SecureHelloWorldService service=new SecureHelloWorldService();
  • SecureHelloWorldPortType port=service.getSecureHelloWorldPortTypePort();
 to
  •  FoobpelClientEp service=new FoobpelClientEp();
  •  FooBPEL port=service.getFooBPELPt();
There you go. You can just type in "ant client run". It will invoke your OSB fooPS proxy! If you run into problems, try use TPCMon to debug the issue.

Of couse, you need to change your user/pass accordingly. Also, I didn't bother to change the client package name etc. This is just a proof of concept thing.

The meat part of the client program is to use BindingPort to set the WSS security header.

With the command line success under my belt, i started to migrate the solution to Web app. Of couse, it would be too easy it simply worked. And that's going to be covered in another post (http://yuanmengblog.blogspot.com/2013/05/eclise-creates-java-web-service-client.html).

I can't seem to find a way to add attachment, so here is the wsdl along with the xsd.

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="fooBPEL" targetNamespace="http://xmlns.oracle.com/cis/foo/fooBPEL" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:plnk="http://docs.oasis-open.org/wsbpel/2.0/plnktype" xmlns:client="http://xmlns.oracle.com/cis/foo/fooBPEL" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:WL5G3N0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
    <plnk:partnerLinkType name="fooBPEL">
        <plnk:role name="fooBPELProvider" portType="client:fooBPEL"/>
    </plnk:partnerLinkType>
<wsp:Policy wsu:Id="wss_username_token_service_policy" xmlns="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<sp:SupportingTokens xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
<wsp:Policy>
<sp:UsernameToken sp:IncludeToken="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/AlwaysToRecipient">
<wsp:Policy>
<sp:WssUsernameToken10/>
</wsp:Policy>
</sp:UsernameToken>
</wsp:Policy>
</sp:SupportingTokens>
</wsp:Policy>
    <wsdl:types>
        <schema xmlns="http://www.w3.org/2001/XMLSchema">
            <import namespace="http://xmlns.oracle.com/cis/foo/fooBPEL" schemaLocation="http://localhost:8011/OWSM_Demo/fooPS?SCHEMA%2FOWSM+Demo%2FfooBPEL"/>
        </schema>
    </wsdl:types>
    <wsdl:message name="fooBPELRequestMessage">
        <wsdl:part name="payload" element="client:process"/>
    </wsdl:message>
    <wsdl:message name="fooBPELResponseMessage">
        <wsdl:part name="payload" element="client:processResponse"/>
    </wsdl:message>
    <wsdl:portType name="fooBPEL">
        <wsdl:operation name="process">
            <wsdl:input message="client:fooBPELRequestMessage"/>
            <wsdl:output message="client:fooBPELResponseMessage"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="fooBPELBinding" type="client:fooBPEL">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
      <wsp:PolicyReference URI="#wss_username_token_service_policy" wsdl:required="false"/>
        <wsdl:operation name="process">
            <soap:operation style="document" soapAction="process"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
    <wsdl:service name="foobpel_client_ep">
        <wsdl:port name="fooBPEL_pt" binding="client:fooBPELBinding">
            <soap:address location="http://thinkpad01:8011/OWSM_Demo/fooPS"/>
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

<?xml version="1.0" encoding="UTF-8"?>
<schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://xmlns.oracle.com/cis/foo/fooBPEL" xmlns="http://www.w3.org/2001/XMLSchema">
 <element name="process">
  <complexType>
   <sequence>
    <element name="input" type="string"/>
   </sequence>
  </complexType>
 </element>
 <element name="processResponse">
  <complexType>
   <sequence>
    <element name="result" type="string"/>
   </sequence>
  </complexType>
 </element>
</schema>

Thursday, April 25, 2013

Tokenize string into node list with XSLT 1.0 and custom XPath function

For XSLT 2.0, use built-in tokenzie function.

For XSLT 1.0, I'll show two ways below.

At the end of the post, i also demonstrate a way to test XSLT from command line directly, it can prove invaluable if you need to debug custom xpath function, like i am doing.

== Part I - use template ==

For xlst 1.0, here is a sample. It shows a recursive template. You can put the template in the same file, but I split the files into two and used import to make the main xsl more clean.

main.xsl

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:car="http://foo/bar">
  <xsl:import href="serialNumber.xsl"/>

 <xsl:template match="/">
                  <xsl:choose>
                      <xsl:when test="contains(/inputStr, ',')=false">
                          <car:Serial>
                            <xsl:value-of select="/inputStr"/>
                          </car:Serial>
                      </xsl:when>
                      <xsl:otherwise>
                          <xsl:call-template name="serial">
                            <xsl:with-param name="commaStr" select="/inputStr"/>
                          </xsl:call-template>
                      </xsl:otherwise>
                  </xsl:choose>
  </xsl:template>
</xsl:stylesheet>

serialNumber.xsl:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:car="http://foo/bar">

  <xsl:template name="serial" >
  <xsl:param name="commaStr"/>
    <xsl:if test="normalize-space($commaStr) != ''">
        <xsl:choose>
            <xsl:when test="contains($commaStr, ',')">
                 <car:Serial>
                    <xsl:value-of select="substring-before($commaStr, ',')"/>    
                  </car:Serial>
                  <xsl:call-template name="serial">
                    <xsl:with-param name="commaStr" select="substring-after($commaStr,',')"/>
                  </xsl:call-template>        
            </xsl:when>
            <xsl:otherwise>
                 <car:Serial>
                    <xsl:value-of select="$commaStr"/>    
                  </car:Serial>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

Disregard the specifics of namespace, here are some sample input and output:

<inputStr>Aaa</inputStr>
results in
<Serial>Aaa</Serial>

<inputStr>Aaa,bbb,ccc</inputStr>
result in
<Serial>Aaa</Serial><Serial>bbb</Serial><Serial>ccc</Serial>

<inputStr>Aaa,bbb,ccc,</inputStr>
will yield the same result.

== part II - use custom java XPath function ==

I developed the project based this link https://blogs.oracle.com/bwb/resource/custom_xpath_functions/Creating_Custom_XPath_functions_with_JDeveloper_FullPost.html, and https://blogs.oracle.com/reynolds/entry/building_your_own_path

My intention is to create a generic tokenizer like Java StringTokenizer. I made it work, but it's mixed result.

I only made it work with XSLT. I still need to figure out how to make it work with BPEL xpath. I suspect there is something not quite right when I pass in a single node to the xpath function whereas the signature expects a List. That's an experiment I need do later. When I do, I'll update this post.

Anyway, here is the java class and the descriptor (just a quick dirty impl for demo with two input parameters):

StringTokenizer.java

package com.foo.util.StringTokenizer;

import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.xml.parsers.DocumentBuilderFactory;
import oracle.fabric.common.xml.xpath.IXPathContext;
import oracle.fabric.common.xml.xpath.IXPathFunction;
import oracle.fabric.common.xml.xpath.XPathFunctionException;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;

public class Tokenizer {
    public static Object tokenizeString(String str, String token) {      
        Element node =  null;
        String xmlStr = "";
        try {
            StringTokenizer st = new StringTokenizer(str, token);
            while (st.hasMoreTokens()) {
                String nt = st.nextToken().trim();
                     System.out.println(nt);
                xmlStr +="<node>"+nt + "</node>";
            }                
            if (xmlStr != null) {
                xmlStr ="<root>" + xmlStr + "</root>";
            }    
            node = DocumentBuilderFactory
                    .newInstance()
                    .newDocumentBuilder()
                    .parse(new ByteArrayInputStream(xmlStr.getBytes()))
                    .getDocumentElement();
     
        }
        catch (Exception e) {
            System.out.println("***ex="+e.getMessage());
        }
        return nList;
    }
    public static void main(String args[]) throws Exception{        
          NodeList nList = (NodeList) Tokenizer.tokenizeString("aaa,bb,cc", ",");
         
          if (nList.getLength() > 0) {
             for (int i = 0; i < nList.getLength(); i++) {
                  Node tNode = nList.item(i);
                  String tc = tNode.getTextContent();
                 System.out.println("***"+tc);              
             }
          }
}
}

descriptor:
<?xml version="1.0" encoding="UTF-8"?>
<soa-xpath-functions
  xmlns="http://xmlns.oracle.com/soa/config/xpath"
  xmlns:tn="http://www.oracle.com/XSL/Transform/java/com.cci.util.StringTokenizer.Tokenizer">
  <function name="tn:tokenize">
    <className>com.cci.util.StringTokenizer.Tokenizer</className>
   <return type="node-set"/>
    <params>
      <param name="str" type="string"/>
     <param name="token" type="string"/>
    </params>
    <desc/>
    <detail>
       <![CDATA[This function breaks up a comma separated string return a tokenized list of nodes.]]>
    </detail>
   </function>  
</soa-xpath-functions>

snippet of test.xsl
...
                xmlns:tn="http://www.oracle.com/XSL/Transform/java/com.foo.util.StringTokenizer.Tokenizer"
...
 <xsl:variable name="sNodes"                      select="tn:tokenizeString(/foo_ESB/DATA/ESB_ORDER/SERIAL_NUMBER_RECEIVED, ',')"/>

<xsl:for-each select="$sNodes">
   <xsl:value-of select="'<serial>'"/>
    <xsl:value-of select="."/>
   <xsl:value-of select="'</serial>"/>          
</xsl:for-each>

testing from the command line

One problem of testing inside JDev XSL mapper is that you can't see your java debug output. You can test your impl from the command line directly. It provides additional error messages if something is wrong.

Here is how I do it in my environment. 

1. set classpath=C:\Oracle\Middleware\oracle_common\modules\oracle.xdk_11.1.0\xmlparserv2.jar;c:\aproj\src\StringTokenizer\deploy\stringTokenizer.jar;C:\Oracle\Middleware\jdeveloper\soa\modules\oracle.soa.fabric_11.1.1\fabric-runtime.jar

2. test with actual input source, and xslt file:
    java oracle.xml.parser.v2.oraxsl    input.xml    test.xsl

3. test with Java only (no xlst):
      java com.foo.util.StringTokenizer.Tokenizer

Monday, November 26, 2012

more notes on java embedding

I noted in the past on BPEL java embedding http://yuanmengblog.blogspot.com/2012/04/few-bpel-java-embedding-notes.html

here are some additional notes:

1. importing syntax:

I'm using BPEL 2.0 this time. This is the importing syntax I'm using now, not sure if it's BPEL 2.0 thing. For sure it's different from my previous post:

add imports right above partner links and right below name spaces.


  <import location="oracle.xml.parser.v2.XMLElement" importType="http://schemas.oracle.com/bpel/extension/java"/>      
  <import location="java.io.File" importType="http://schemas.oracle.com/bpel/extension/java"/>      

2. Two ways to get and set variables:

2.1. getting and setting string variables:
   nothing fancy, you can do straight:
    String foo = (String) getVariableData("foo");
    setVariableData("foo", "bar");

2.2. If you need to get to an Xpath in an element, then you have to do something like

XMLElement srcElem = (XMLElement) getVariableData("inputVariable", "payload", "/client:foo/client:bar");      
String bar = srcElem.getTextContent();      

remember you need to add "import" like in step 1. 

3. Gotcha's

Although it's very tempting, don't do this:
   String srcElem = (String) getVariableData("inputVariable", "payload", "/client:foo/client:bar");   
it doesn't work that way.

If you don't want to deal with the hassles of "importing" and casting, here is a recommended way to go around it. 
Using above example:
 a) create a BPEL string variable, call it "bar"
 b) inside BPEL, assign ("inputVariable", "payload", "/client:foo/client:bar") to the "bar" variable
 c) inside your java embedding, you can use get/set freely on "bar", just like in step 2.1 above.

Friday, October 19, 2012

PGP and SOA

My environment:
      SOA 11.1.1.6
      Java 1.6.x
Goal:
      sftp adapter to grab a PGP encrypted file, then decrypt the file in the SOA composite then process the data. You will be supplied with a PGP private key file (binary key ring, or ascii key), and pass phrase to decode the file.

My solution:
     use java embedding to decrypt PGP file. In essence, it's a Java solution.

Java part:

Here is how to do it in Java:
1. go to Bounce Castle to download the latest jars


Download
 bcpg-jdk15on-147.jar 
bcprov-jdk15on-147.jar 

2. online resource to use the package to encrypt/decrypt:


3. I added a simple main() to the above PGPFileProcessor class:

public static void main(String args[]) throws Exception
{
   PGPFileProcessor pgp = new PGPFileProcessor();

   //pgp.setAsciiArmored(true); // if you want dump ascii file
   // hard code for my test
   pgp.setInputFileName("c:/pgp/java/sample_file.txt");
   pgp.setOutputFileName("c:/pgp/java/sample_file.txt.pgp");
   pgp.setPublicKeyFileName("c:/pgp/java/mypgp-pub.key"); //can be either binary or text key
   pgp.encrypt();

// decrypt the same file
   pgp.setInputFileName("c:/pgp/java/sample_file.txt.pgp");
   pgp.setOutputFileName("c:/pgp/java/sample_file-decrypted.txt");
   pgp.setSecretKeyFileName("c:/pgp/java/mypgp-pri.key"); // can be either binary or text key
   pgp.setPassphrase("mypassword");
   pgp.decrypt();
}

3. set your class path to include the jars in step 1, and compile java files from step 2

4. if you get java exceptions like:
PGPKeyRingTest: exception: java.security.InvalidKeyException: Illegal key size

You may need to download

Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 6


save the jars to “C:\java\jdk1.6.0_31\jre\lib\security”. Backup local_policy.jar, and US_export_policy.jar first!


5. package the compiled classes into a jar, let's call it mypgp.jar

Add Java to the composite:

1. copy  bcpg-jdk15on-147.jar, bcprov-jdk15on-147.jar and mypgp.jar (you just generated) to two locations (you need to figure out your own soa environment)
C:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.ext_11.1.1
and
C:\Oracle\Middleware\user_projects\domains\soa_domain\lib


For the first copied location, there is build.xml in that directory already, you need to run "ant" command in that directory. If you don't want to run "ant" command, you can also unjar your files into the "classes:" sub directory.
After you finish that, you need to bounce the entire SOA suite (including weblogic server).

2. code embedded java:
            <![CDATA[System.out.println("**********new pgp");      
pgp.test.com.PGPFileProcessor pgp = new pgp.test.com.PGPFileProcessor();      
      
System.out.println("*end *********new pgp");      
     
XMLElement srcElem = (XMLElement) getVariableData("inputVariable", "payload", "/client:myReqeust/client:srcFile");    
String srcFile = srcElem.getTextContent();    
String tgtFile = srcFile.substring(0, srcFile.lastIndexOf(".pgp"));    
      
args[0] = ;      
args[1] = ;      
      
      
pgp.setInputFileName(new String("/soaIntegration/pgp/receive/") + srcFile);      
pgp.setOutputFileName(new String("/soaIntegration/pgp/process/") + tgtFile);      
pgp.setSecretKeyFileName("/soaIntegration/pgp/pri-key.key");      
pgp.setPassphrase("mypassword");      
      
try {      
pgp.decrypt();      
}      
catch(Exception e)      
{      
  System.out.println("###PGP decryption failed:"+e.getMessage());      
}]]>

Additional: generate your own key pairs for test or for fun

What if you want to generate your own key pairs for test?  Here is what I did:
download "bcpg-jdk15on-147.zip" with source, then find this file PGPKeyRingTest.java in package org.bouncycastle.openpgp.test.

I modify generatetest() like below:
    public void generateTest()         throws Exception
    {
System.out.println("********* intercepted ********");
        char[]              passPhrase = "hello".toCharArray();
        KeyPairGenerator    dsaKpg = KeyPairGenerator.getInstance("DSA", "BC");
        dsaKpg.initialize(512);
...
PGPPublicKeyRing        pubRing = keyRingGen.generatePublicKeyRing();
        PGPPublicKey            vKey = null;
        PGPPublicKey            sKey = null;

  byte[] b = null;
            ByteArrayOutputStream baos = null;
            ArmoredOutputStream aos = null;
            try {
                    baos = new ByteArrayOutputStream();
                    aos = new ArmoredOutputStream(baos);
                    // get public key
                    pubRing.encode(aos);
                    aos.flush();
                    baos.flush();
                    aos.close() ;
                    b = baos.toByteArray();
                    System.out.println(new String(b));

                    baos = new ByteArrayOutputStream();
                    aos = new ArmoredOutputStream(baos);
                    // get private key
                    keyRing.encode(aos);
                    aos.flush();
                    baos.flush();
                    aos.close() ;
                    b = baos.toByteArray();
                    System.out.println(new String(b));

            } catch (Exception e) {
                    System.out.println("Exception caught while exporting SecretKeyRing"+ e);
            } finally {
                    aos.close();
                    baos.close();
            }
of course, i modified performTest() only to run generatetest().

you need to add bctest-jdk15on-147.jar to the classpath to run the above class. 

If you get java exceptions compiling or running, check step 4 above in the Java section.

There you go, you'll get your own public and private keys to play with (remember your password is "hello" in the above code, you can change it if you like).

PGP Command Line

BTW, if you happen to have the PGP command line from old PGP Corporation (currently owned by Symatec), you can generate your own keys, and test encryption and decryption from the command line.

To generate key:
   pgp --gen-key "foo@bar.com" --key-type rsa --bits 2048 --passphrase car

export the key:
     pgp --export-key-pair foo

To test encryption/decryption:
  pgp -e test.txt --recipient foo (or pgp -e test.txt --recipient foo --armor)

  pgp --decrypt test.txt.pgp  --passphrase "car"--output foo.txt

if you wonder where pgp command line stores key ring files, they are under here on windows: C:\Documents and Settings\yourUserName\My Documents\PGP. I was using the binary key rings initially before I figured out the export commands.

That's all

I assumed anyone reading this post has the basic idea how PGP works. What I demonstrated is how to use Java to decrypt a PGP file, and add the java solution to SOA composite. 





Thursday, September 27, 2012

Adding custom java classes to SOA

1. go to SOA ext folder, in my case: C:\Oracle\Middleware\Oracle_SOA1\soa\modules\oracle.soa.ext_11.1.1

2. you have two ways to do it:

2.1 explode your jars under "classes" sub folder here
2.2 copy the jar files in this folder, then in command prompt, run "ant" (this folder should have a default build.xml file), it basically does some magic and your jars will be included in SOA. As for the "magic", I believe it simply updates oracle.soa.ext.jar file and modified the "Manifiest" under "META-INF", set something like "Class-Path: yourCustomer.jar classes/"

Keep in mind, that push to shove, you may also have to add the same jar to  "$DOMAIN_DIR/lib", that you  just drop the file in.

I experienced problem before that merely putting jar in "racle_SOA1\soa\modules\oracle.soa.ext_11.1.1" works fine for SOA run time (with your BPEL referencing the class in the jar). However, if you don't copy the same file under "$DOMAIN_DIR/lib", you cannot re-deploy your BPEL composite that referencing the custom class. Because BPELCompiler, 2nd pass compilation (during the composite deployment),  seems to require the jar to be in "$DOMAIN_DIR/lib" folder. Go figure...

Tuesday, April 24, 2012

A Few BPEL Java Embedding Notes

1. Error: SCAC-50012
    check \SCA-INF\classes\scac.log file to find more details.

    Common issues:

    #1.1) check that you added the proper import at the top of .bpel file
     
     for example (right after <:process ...> element, and before partner links   
     <bpelx:exec import="java.util.*"/>
    <bpelx:exec import="java.lang.*"/>
    <bpelx:exec import="java.math.*"/>
    <bpelx:exec import="org.w3c.dom.Element"/>
    <bpelx:exec import="oracle.xml.parser.v2.*"/>   
    <bpelx:exec import="com.foo.bar.*"/>
    <bpelx:exec import="com.collaxa.cube.ws.wsif.providers.java.*"/>

     #1.2) check your syntax carefully. When in doubt, comment out as much as you can.

2. incorparte your own special classes and jars
    create your class jar file, place it under "SCA-INF/lib" folder

3. get/set your variables
    For simple string variables, it's straight forward. If you need to process the XML payloads, use forms like this:
 oracle.xml.parser.v2.XMLElement targetElem =
    (oracle.xml.parser.v2.XMLElement) getVariableData("myVarName", "payload",  "/ns3:foo/ns3:bar/ns3:car");  where "ns3" is defined precisely as it appears at the top of your .bpel file.

you can manipulate this element with functions, here are a few common functions: "getParentNode, getTextContent, cloneNode, setTextContent, insertBefore" etc.

4. if you need to debug, add some log entries using "addAuditTrailEntry".