Pages

Sunday, August 1, 2010

SOAP over JMS with WebLogic

With WebLogic 10.3 you can also do SOAP over JMS instead of HTTP. In this blogpost you will see how you can achieve this with WebLogic JMS and with JDeveloper as IDE. The JMS transport offers the following benefits: reliability, scalability, and quality of service but it does require slightly more overhead and programming complexity than HTTP. And you can't use JAX-WS, JAX-WS on WebLogic can't use any other protocol then HTTP, you need to use JAX-RPC instead. This leads to a second problem, JAX-RPC is not well supported in JDeveloper wizards so you need to use ANT.

First step is to configure JMS. I will show you the steps on the Integrated WebLogic of JDeveloper.
Create a persistent Store. You can use a normal FileStore and target this to the DefaultServer.
Create a JMS Server and use the FileStore and also target this to the DefaultServer.
Create a JMS Module and target this to the DefaultServer.
Create a Sub Deployment ( part of your JMS Module ) and target this to your JMSServer.
Now you can create some resources for this JMS Module. You need to create a Connection Factory and use your Sub Deployment. Create a JMS Queue and also use the same Sub Deployment. The JNDI Names are very important you need this in your JAX-RPC Web Service Class.
The WebLogic JMS part is ready, you can restart the WebLogic Server from JDeveloper and check if you see the JNDI Resources in the DefaultServer ( Go to Servers in the WebLogic Console , Click the DefaultServer, on the top of the page there is a JNDI hyperlink )

In JDeveloper you can create a new Workspace / Project.
Create a new Java Class which will be our JAX-RPC Web Service class. Here you need to add the WLJmsTransport Annotation with the queue and connectionFactory attribute. You can't use the forward slash in the JDNI name so replace this with a dot. The rest is like a normal Web Service in JAX-WS or JAX-RPC.
package nl.whitehorses.ws.jms;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;

import weblogic.jws.WLJmsTransport;
@WebService(portName = "HelloWorldJMSPort",
            serviceName = "HelloWorldJMSService")

@WLJmsTransport(serviceUri="HelloWorldJMSService",
                contextPath="HelloWorldJMSService",
                queue="jms.SoapQueue", 
                portName="HelloWorldJMSPort", 
                connectionFactory="jms.CF") 

public class HelloWorld {

    @WebMethod
    @WebResult(name = "result")
    public String sayHello(@WebParam(name = "message") String message ) {
      
      return message;
    }
}
Next step is to deploy this to the WebLogic Server. You need to do this with ANT. Add an empty build file to your project and the Weblogic jar to the ANT classpath.
Open the build.xml and add the following.
<project name="webservices-hello_world" default="all">

 <taskdef name="jwsc" classname="weblogic.wsee.tools.anttasks.JwscTask"/>

 <target name="build-service">
  <jwsc srcdir="src" destdir="deploy/helloWorldEar">
   <jws file="nl/whitehorses/ws/jms/HelloWorld.java" type="JAXRPC"/>
  </jwsc>
 </target>

 <taskdef name="wldeploy" classname="weblogic.ant.taskdefs.management.WLDeploy"/>


 <property name="wls.username" value="weblogic"/>
 <property name="wls.password" value="weblogic1"/>
 <property name="wls.hostname" value="localhost"/>
 <property name="wls.port" value="7101"/>
 <property name="wls.server.name" value="DefaultServer"/>

 <target name="deploy">
  <wldeploy action="deploy" name="helloWorldEar" source="deploy/helloWorldEar"
            user="${wls.username}" password="${wls.password}" verbose="true"
            adminurl="t3://${wls.hostname}:${wls.port}"
            targets="${wls.server.name}"/>
 </target>

 <taskdef name="clientgen" classname="weblogic.wsee.tools.anttasks.ClientGenTask"/>

 <target name="client">
  <clientgen wsdl="http://${wls.hostname}:${wls.port}/HelloWorldJMSService/HelloWorldJMSService?WSDL"
             destdir="src" 
             packagename="nl.whitehorses.client.jms"
             type="JAXRPC"/>
  <javac srcdir="src" 
         destdir="classes"
         includes="nl/whitehorses/client/jms/**/*.java"/>
 </target>

</project>
First step is to make an deployment with the JAXRPC option. Run the build-service target. After that you can run the deploy target.
If everything went well, you can go to the WebLogic deployments. 
 Select the Web Service and go to the Testing Tab. You can't test this Web Service with this Test Client.
 Select the WSDL hyperlink and take a look at the endpoint.
The last part is to test this Web Service. For this you need to generate a Web Service Proxy client. Run the client target in ANT. After that you can make your own TestClient where you will be using these client classes.
package nl.whitehorses.client.jms;

import java.rmi.RemoteException;

import javax.xml.rpc.ServiceException;
import javax.xml.rpc.Stub;

import weblogic.wsee.jaxrpc.WLStub;

public class TestService {
    public TestService() throws RemoteException, ServiceException {

        HelloWorldJMSService service = new HelloWorldJMSService_Impl(
           "http://localhost:7101/HelloWorldJMSService/HelloWorldJMSService?WSDL");

        HelloWorld port = service.getHelloWorldJMSPort();

        Stub stub = (Stub)port;

        stub._setProperty(WLStub.JMS_TRANSPORT_JNDI_URL,"t3://localhost:7101");
 

        try {
            String result = null;
            result = port.sayHello("Hello");
            System.out.println("Got JMS result: " + result);

        } catch (RemoteException e) {
            throw e;
        }

    }

    public static void main(String[] args) {
        TestService testService;
        try {
            testService = new TestService();
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (ServiceException e) {
            e.printStackTrace();
        }
    }
}

Here you can download my JDeveloper 11g Workspace.

Extra update from Ales.

When you want to use both one for SOAP/HTTP and also for SOAP/JMS you need to define a port for each communication protocol and it need to have its own service.

you can use the following logging options.

-Dweblogic.wsee.verbose=weblogic.wsee.connection.transport.jms.*
-Dweblogic.wsee.verbose= weblogic.wsee.server.jms.*

This shows which JMS listeners are instantiated and on which ports they are listening.

Monday, July 26, 2010

ADF Task Flow Region interaction with Parent Action activity

When you use Task Flows in your ADF 11g Web Application then you probably need to have some ADF Region interaction with other Regions or its containing JSF page. In this blogpost I will show you can control a dynamic Region from a other Region ( Task Flow ) with a Parent Action Activity. Off course you change the bean used for the dynamic region from backing bean scope to session bean scope and call this bean from every Task Flow. This works perfectly but you ADF has a better solution for this and you don't need to change the scope of the dynamic Task Flow bean.

For this blogpost I made an Example with one JSPX page and this page contains two Regions. The first is the Top TF and the second is a dynamic Region with Center 1 & 2 TF. Start is part of the JSPX page and can call the Center 1 & 2 methods directly in the dynamic region bean.
For showing the Center Task Flows from the Top Task Flow you can use the Parent Action activity. 

Here is the code of the main JSPX page with in the start facet ,the two buttons which can call the backingbean methods.
<?xml version='1.0' encoding='UTF-8'?>
<jsp:root xmlns:jsp="http://java.sun.com/JSP/Page" version="2.1"
          xmlns:f="http://java.sun.com/jsf/core"
          xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
  <jsp:directive.page contentType="text/html;charset=UTF-8"/>
  <f:view>
    <af:document id="d1" title="Example">
      <af:form id="f1">
        <af:panelStretchLayout id="psl1" startWidth="239px" topHeight="95px">
          <f:facet name="center">
            <af:region value="#{bindings.dynamicRegion1.regionModel}" id="centerRegion"/>
          </f:facet>
          <f:facet name="start">
            <af:panelHeader text="Start" id="ph1" inlineStyle="width:239px; height:423px;">
              <f:facet name="toolbar">
                <af:toolbar id="t1">
                  <af:commandToolbarButton text="Center1"
                                           id="ctb1"
                                           action="#{backingBeanScope.MainRegionHandler.showCenter1TF}"/>
                  <af:commandToolbarButton text="Center2"
                                           id="ctb2"
                                           action="#{backingBeanScope.MainRegionHandler.showCenter2TF}"/>
                </af:toolbar>
              </f:facet>
            </af:panelHeader>
          </f:facet>
          <f:facet name="top">
            <af:region value="#{bindings.top1.regionModel}" id="centerTop"/>
          </f:facet>
        </af:panelStretchLayout>
      </af:form>
    </af:document>
  </f:view>
</jsp:root>
The dynamic region bean with the showCenter1TF and showCenter2TF methods.
package nl.whitehorses.adf.tf.view.beans;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;

import oracle.adf.controller.TaskFlowId;
import oracle.adf.view.rich.context.AdfFacesContext;

public class MainRegionHandler {

    private String taskFlowIdCenter1 = "/WEB-INF/center1.xml#center1";
    private String taskFlowIdCenter2 = "/WEB-INF/center2.xml#center2";
    private String taskFlowId = taskFlowIdCenter1;

    public MainRegionHandler() {
    }

    public TaskFlowId getDynamicTaskFlowId() {
        return TaskFlowId.parse(taskFlowId);
    }

    public String showCenter1TF() {
        taskFlowId = taskFlowIdCenter1;
        AdfFacesContext.getCurrentInstance().addPartialTarget(getUIComponent("centerRegion"));  
        return null;
    }

    public String showCenter2TF() {
        taskFlowId = taskFlowIdCenter2;
        AdfFacesContext.getCurrentInstance().addPartialTarget(getUIComponent("centerRegion"));  
        return null;
    }

    private UIComponent getUIComponent(String name) {
        FacesContext facesCtx = FacesContext.getCurrentInstance();
        return facesCtx.getViewRoot().findComponent(name);
    }

}
From the Start facet on the main page it is easy to show the right Center Task Flow. From the Top Task Flow you need to do more. First you need to change the parent Task Flow. ( this works in a bounded or unbounded Task Flow ). Add two Method Call activities and a Wildcard Control Flow Rule.
You will later call the goCenter1 and goCenter2 Control Flow cases  from the Top Task Flow. This will activate the method call which call the right method in the dynamic region bean. After a successful invocation, it will use the return Control Flow case to return to the page.
The property window of the showCenter1TF Method Call activity looks like this. Here you need to provide the Method value and provide the Fixed Outcome.
Next step is to change the Top Task Flow where you also need to add a Wildcard Control Flow Rule together with two Parent Action activities.
The Parent Action property window looks like this. Here you need to provide the Parent Outcome , this must match with the Control Flow Case of the parent Task Flow and here is the Outcome also return.


And at last the Top fragment with the two buttons which calls the right Control Flow Case.
<?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:panelHeader text="Top TF" id="ph1"
                  inlineStyle="width:995px; height:84px;">
              <f:facet name="toolbar">
                <af:toolbar id="t1">
                  <af:commandToolbarButton text="Center1"
                                           id="ctb1"
                                           action="showCenter1TF"/>
                  <af:commandToolbarButton text="Center2"
                                           id="ctb2"
                                           action="showCenter2TF"/>
                </af:toolbar>
              </f:facet>
  </af:panelHeader>
</jsp:root>
That's all and here is the example workspace.

Thursday, July 15, 2010

OSB 11g ANT deployment scripts

For Oracle Service Bus 10.3 & 11g you can use the ANT / WLST deployment scripts provided by Oracle. With these scripts you can make an export 1 or more OSB projects from sbconsole, make a deployment / configuration plan and you can import a project jar to the sbconsole.

I made some changes to these scripts so it only needs one property file (build.properties ), you don't need to use the import or export.properties files. The second change I made, is support for making an export jar from the OSB workspace ( OEPE environment ). Very important! Do this on a copy of your workspace or use subversion because maybe you need to delete the .metadata folder for a successful export. ( you will loose all your workspace settings )

When you want to do this with Maven instead of ANT you can use these scripts and poms https://github.com/biemond/soa_tools/tree/master/maven_osb_ps5
or for PS6
https://github.com/biemond/soa_tools/tree/master/maven_osb_ps6
or with the new PS6 configjar tool which can create offline OSB exports without OEPE http://biemond.blogspot.nl/2013/04/offline-oracle-service-bus.html 

Here is the build.properties.
The ANT build.xml
When you want to run the ANT task from CMD then you need these environment variables
You can use these ANT tasks
ant exportFromWorkspace ( do this on a copy of your OEPE workspace )
ant importToOSB
ant exportFromOSB

Or you can run these task in JDeveloper, then you need to update these classpath entries for ANT


And run your ANT task from JDeveloper.

Download the PS5 or PS6 source code on my github https://github.com/biemond/soa_tools