Tuesday, June 16, 2009

Auto-refreshing ADF chart objects in JDev 11g

BTW. Today is Bloomsday - James Joyce abù

This sample uses JDev 11g and ORCL DB11g

In this simple scenario I will detail how to auto refresh a pie chart based on the following query. We will then amend the query where clause to exclude a particular department.




--------------------------------------------------------

SELECT sum(Emp.sal) SalaryTotal,
Dept.deptno,
Dept.dname

FROM Emp, Dept

WHERE Emp.deptno = Dept.deptno

GROUP BY Dept.Dname, Dept.Deptno
--------------------------------------------------------

Pre-requisite on the DB Side

in SQLPLUS --> conn system/manager

grant change notification to scott;

Step 1 - Creating the Model

Create a new Fusion Web Application (ADF) in JDev 11g

In the Model Project - Create new ADF Business Components --> Business Components from Tables

Create a connection to SCOTT's schema on your 11g DB and create Entity Objects based on the tables - Dept & Emp



Create the ViewObjects -



Accept the defaults on the following pages.



Create a new ViewObject for the Chart


Enter the following query on the next page-

SELECT sum(Emp.sal) SalaryTotal,
Dept.deptno,
Dept.dname

FROM Emp, Dept

WHERE Emp.deptno = Dept.deptno

GROUP BY Dept.Dname, Dept.Deptno




Accept defaults on the following steps.
At step 7 - select checkbox - Generate View Object Class: SalByDeptViewImpl



At Step 8 - select checkbox to add the new view to the Application Module



For the new view - Set auto-refresh property = true




Copy and paste the following code into the ...impl class you generated earlier
(after the constructor)

long lastRequery = 0;
public long getLastRequery() {
return lastRequery;
}
@Override
protected void bindParametersForCollection(QueryCollection queryCollection,
Object[] object,
PreparedStatement preparedStatement) throws SQLException {
super.bindParametersForCollection(queryCollection, object, preparedStatement);
if (queryCollection != null) {
lastRequery = System.currentTimeMillis();
}
System.out.println("Re-execute "+(queryCollection == null?"count ":"")+"query for VO "+getName());
}

@Override
protected void processDatabaseChangeNotification(QueryCollection queryCollection) {
lastRequery = System.currentTimeMillis();
super.processDatabaseChangeNotification(queryCollection);
}
}



We have just done the following -

We have created a long value lastRequery
We have overridden the following methods –
-bindParametersForCollection
-processDatabaseChangeNotification

The long value represents the last time the query was executed or it has handled a database change notification event. The value of this long has been exposed so the client can reference it as a custom method, see the getLastRequery() method
bindParametersForCollection() and processDatabaseChangeNotification() update this flag.

Expose the getLastRequery() method via the client interface



Step 2 - Creating the UI

Create a new JSF page via the faces-config.xml (graphical mode)



Double-click on the image to create the page, selecting auto-creation of backing bean.


You are now in the visual editor.

-Drag and Drop a Poll control onto the page.



-Drag and Drop SalByDeptView as a Graph --> Pie Chart onto the page







-Drag and Drop getLastRequery()as a method somewhere on the page




-Edit the jspx source and comment out the button you just dropped as we only need it's binding.




-Go to the pagedef file's overview.




-Click on the SalByDeptView iterator binding.
- In the Property Inspector, change the rangeSize to 100



- Edit the page by selecting the graph component in the structure pane

In the Property Inspector set the ContentDelivery flag to "immediate".



Now we need to poll on lastRequery()
So add the following method to the end of the page's backing bean.

public void onPollTimerExpired(PollEvent pollEvent) {
Long lastRequery = (Long)AdfFacesContext.getCurrentInstance().getViewScope().get("lastRequery");
Long lastRequeryFromVO = (Long)BindingContext.getCurrent().getCurrentBindingsEntry().getOperationBinding("getLastRequery").execute();
System.out.println("Timer expired: lastRequery = "+lastRequery+", lastRequeryFromVO = "+lastRequeryFromVO);
if (lastRequery == null (!lastRequery.equals(lastRequeryFromVO))) {
AdfFacesContext.getCurrentInstance().getViewScope().put("lastRequery",lastRequeryFromVO);
AdfFacesContext.getCurrentInstance().addPartialTarget(getPieGraph1());
System.out.println("Data requeried in VO. PPR'ing the pie chart");
}
}

-Select the Poll operation in the structure window
-- Set the Id to pollTimer
-- Select onPollTimerExpired() in the PollListener propety
-- Set the Interval property to 2000




The onPollTimerExpired poll listener event handler invokes a method action binding to get the lastRequery() long value. It stores this value in the view scope. If it notices that the value has changed, then it PPR's the pie chart.

- In the DataBindings.cpx file, for the "AppModuleDataControl" data control usage, set the configuration to use the "AppModuleShared" configuration. This configuration is automatically created for you by ADF design time, along with the "AppModuleLocal" configuration. Auto-refresh view objects only work when they are in a shared AM.



Step 3 - Test

Run the page from the faces-config.xml diagrammer



Open a cmd window and start sqlplus as scott

Insert a new emp with a large salary e.g.
insert into emp values (
8881,
'Jim',
null,
null,
sysdate,
9999,
100,
30);

commit;

The pie chart will be updated automatically.



Now we will amend the WHERE clause to ignore Department 10.

Step 4 - Dynamically Changing the WHERE clause

To avoid getting an ORA-29983

ORA-29983: Unsupported query for Continuous Query Notification
Cause: The query cannot be registered for Continuous Query Notification.
Action: The query has some constructs that make it incompatible with Continous Query Notification like synonyms or views. Please check the documentation for complete list.

we must deactivate the default use of inline views by calling setNestedSelectForFullSql()
in the create() method of the ...viewImpl class.

Open SalByDeptViewImpl.
We must override the create method so Menu --> Source --> Override Methods.
Select the create() method
Add the following after the line super.create();
this.setNestedSelectForFullSql(false);

We will now add a method to amend the WHERE clause to ignore Department 10.

public void excludeDept10(){
ViewObject vo = this.getViewObject();

// get the original query
String query = vo.getQuery();
System.out.println("VO query is = " + query);

// let's find the WHERE clause
int startOfWhere = query.indexOf("WHERE");
int startOfGroupBy = query.indexOf("GROUP BY");
String whereClause = query.substring(startOfWhere, startOfGroupBy);
System.out.println("Original WHERE Clause = " + whereClause);

// amend the WHERE clause to exclude Dept 10
whereClause = whereClause + " and Dept.deptno > 10 ";
String newQuery = query.substring(0, startOfWhere-1);
newQuery = newQuery + whereClause;
System.out.println("New Query with WHERE = "+ newQuery);

newQuery = newQuery + query.substring(startOfGroupBy, query.length());
System.out.println("New Query with Group BY = "+ newQuery);

this.setQuery(newQuery);
vo.executeQuery();
}

I'm sure there's a much more elegant way on doing this --)
- Expose this method (as we did getLastRequery()) via the client interface
- Refresh the DataControl
- Drop the method as an ADF button on the page

- re-test



Press the button to exclude Dept 10



- Enter a new Emp with a large salary




Tuesday, March 24, 2009

Installing MLR#3 on SOA Suite 10.1.3.4

Unzip MLR3 patch to a directory of your choice.
copy the directory 7586063 to the soaSuiteHome\opatches directory

Open a CMD window in this directory

set Oracle_home=d:\soa10134\appserver
set PERL5LIB=d:\soa10134\appserver\perl\5.8.3\lib
set OPATCH_PLATFORM_ID=0
set path=d:\soa10134\appserver\OPatch;%path%

Apply the patch -
opatch apply

Post-Steps
DB Schema upgrade

ORABPEL- according to the patch README, if you're pure 10.1.3.4 then you must run the following to update the schema
upgrade_10134_10134mlr_above_mlr11_oracle.sql
In my case the file is located at -
D:\SOA10134\AppServer\bpel\system\database\scripts

ORAESB - run the following -
SQL> @$ORACLE_HOME/integration/esb/sql/oracle/upgrade_10134_10134MLR.sql

In my case the file is located at -
D:\SOA10134\AppServer\integration\esb\sql\oracle

XREF: xREF Schema update. I've not used ESB xrefs so they don't need updating

Restart Soa Suite 10.1.3.4
opmnctl startall

Tuesday, March 10, 2009

Calling async BPEL process from JDEV 11g

Here is a simple example

· Create a simple asynchronous BPEL process
o Accept a String as input
o Do some simple processing and return the string as output



· Deploy to BPEL
· Get BPEL wsdl




· Create a new Application & Project in JDev 11g
In the project, create a Web Service Proxy



· Select Initiate Service à SimpleAsyncHello



· Click Finish

· The following artifacts are created



The 2 interesting files in respect of this lab are –

· SimpleAsyncHelloPortClient
o à Initiate BPEL process

· SimpleAsyncHelloCallbackImpl
o à Receive Callback from BPEL Server

As mentioned in the JDEV 11g release notes, we will have to make some changes to the SimpleAsyncHelloPortClient as BPEL 10.1.3.4 uses 2003 style addressing.


Original SimpleAsyncHelloPortClient –

public static void main(String [] args)
{
simpleAsyncHello_Service = new SimpleAsyncHello_Service();
SimpleAsyncHello simpleAsyncHello = simpleAsyncHello_Service.getSimpleAsyncHelloPort();
// Get the request context to set the outgoing addressing properties
WSBindingProvider wsbp = (WSBindingProvider)simpleAsyncHello;
WSEndpointReference replyTo =
new WSEndpointReference("http://", WS_ADDR_VER);
String uuid = "uuid:" + UUID.randomUUID();

wsbp.setOutboundHeaders( new StringHeader(WS_ADDR_VER.messageIDTag, uuid), new StringHeader(WS_ADDR_VER.actionTag, "action" ), replyTo.createHeader(WS_ADDR_VER.replyToTag));

// Add your code to call the desired methods.
}



Revised SimpleAsyncHelloPortClient –

· Imports

import com.oracle.xmlns.simpleasynchello.SimpleAsyncHello;
import com.oracle.xmlns.simpleasynchello.SimpleAsyncHello_Service;

import com.sun.xml.ws.api.addressing.AddressingVersion;
import com.sun.xml.ws.api.addressing.WSEndpointReference;
import com.sun.xml.ws.developer.WSBindingProvider;
import com.sun.xml.ws.message.StringHeader;

import java.util.UUID;

import javax.xml.namespace.QName;
import javax.xml.ws.WebServiceRef;

import org.xmlsoap.schemas.ws._2003._03.addressing.AttributedURI;
import org.xmlsoap.schemas.ws._2003._03.addressing.EndpointReferenceType;

· Main method

public static void main(String [] args)
{
simpleAsyncHello_Service = new SimpleAsyncHello_Service();
SimpleAsyncHello simpleAsyncHello = simpleAsyncHello_Service.getSimpleAsyncHelloPort();
// Get the request context to set the outgoing addressing properties
WSBindingProvider wsbp = (WSBindingProvider)simpleAsyncHello;

String uuid = "uuid:" + UUID.randomUUID();

// Add your code to call the desired methods.
//
AttributedURI messageId = new AttributedURI();
messageId.setValue( "uuid:" + UUID.randomUUID() );

// prepare ReplyTo
AttributedURI address = new AttributedURI();
address.setValue("http://localhost:7101/MyBPELAsyncClient-MyBPELClient-context-root/SimpleAsyncHelloCallbackPort");
EndpointReferenceType replyTo = new EndpointReferenceType();
replyTo.setAddress( address );

// prepare action header
wsbp.setOutboundHeaders(new StringHeader(
new QName( "http://schemas.xmlsoap.org/ws/2003/03/addressing", "Action" ),
"http://xmlns.oracle.com/SimpleAsyncHello/SimpleAsyncHello/initiate" ));

// Prepare payload
SimpleAsyncHelloProcessRequest asyncHelloProcessRequest = new SimpleAsyncHelloProcessRequest();
asyncHelloProcessRequest.setInput("Hi There");
simpleAsyncHello.initiate(asyncHelloProcessRequest, replyTo,messageId );

}

So what have we done here?

· Prepare ReplyTo:
· http://localhost:7101/MyBPELAsyncClient-MyBPELClient-context-root/SimpleAsyncHelloCallbackPort
o BPEL will reply to a service running on WLS. This service has been generated for us SimpleAsyncCallbackImpl.
o To test this, simply select this class and Right-mouse Click Run.This will deploy it to the embedded WLS that comes with JDev.

· Test the URL in a browser




· Check the wsdl for the port location




· http://localhost:7101/MyBPELAsyncClient-MyBPELClient-context-root/SimpleAsyncHelloCallbackPort


· Prepare Action Header (for 2003 compatability)
wsbp.setOutboundHeaders(new StringHeader(
new QName( "http://schemas.xmlsoap.org/ws/2003/03/addressing", "Action" ),
"http://xmlns.oracle.com/SimpleAsyncHello/SimpleAsyncHello/initiate" ));

· Prepare Payload (BPEL process input) and invoke the initiate method
SimpleAsyncHelloProcessRequest asyncHelloProcessRequest = new SimpleAsyncHelloProcessRequest();
asyncHelloProcessRequest.setInput("Hi There");
simpleAsyncHello.initiate(asyncHelloProcessRequest, replyTo,messageId );


· Test
o Run SimpleAsyncHelloPortClient
o Check BPEL Console




So now it’s time to look at the implementation of the Callback

· Original SimpleAsyncHelloCallbackImpl


package com.oracle.xmlns.simpleasynchello;

import com.sun.xml.ws.api.addressing.AddressingVersion;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.HeaderList;
import com.sun.xml.ws.developer.JAXWSProperties;

import javax.annotation.Resource;

import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.ParameterStyle;
import javax.jws.soap.SOAPBinding.Style;

import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.Action;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.soap.Addressing;
// !THE CHANGES MADE TO THIS FILE WILL BE DESTROYED IF REGENERATED!
// This source file is generated by Oracle tools
// Contents may be subject to change
// For reporting problems, use the following
// Version = Oracle WebServices (11.1.1.0.0, build 080922.1045.35800)

@WebService(targetNamespace="http://xmlns.oracle.com/SimpleAsyncHello",
name="SimpleAsyncHelloCallback")
@XmlSeeAlso(
{ org.xmlsoap.schemas.ws._2003._03.addressing.ObjectFactory.class, com.oracle.xmlns.simpleasynchello.ObjectFactory.class })
@SOAPBinding(style=Style.DOCUMENT, parameterStyle=ParameterStyle.BARE)
@Addressing(enabled=true, required=true)
public class SimpleAsyncHelloCallbackImpl
{
@Resource
private WebServiceContext wsContext;

private static final AddressingVersion WS_ADDR_VER = AddressingVersion.W3C;

@WebMethod(action="onResult")
@SOAPBinding(parameterStyle=ParameterStyle.BARE)
@Action(input="onResult")
@Oneway
public void onResult(@WebParam(targetNamespace="http://xmlns.oracle.com/SimpleAsyncHello",
partName="payload", name="SimpleAsyncHelloProcessResponse")
com.oracle.xmlns.simpleasynchello.SimpleAsyncHelloProcessResponse payload,
@WebParam(targetNamespace="http://schemas.xmlsoap.org/ws/2003/03/addressing",
partName="RelatesTo", name="RelatesTo", header=true)
org.xmlsoap.schemas.ws._2003._03.addressing.Relationship RelatesTo)
{
// Use the sample code to extract the relatesTo id for correlation and then add your rest of the logic

System.out.println("Received the asynchronous reply");

// get the messageId to correlate this reply with the original request
HeaderList headerList = (HeaderList)wsContext.getMessageContext().get(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY);
Header realtesToheader = headerList.get(WS_ADDR_VER.relatesToTag, true);
String relatesToMessageId = realtesToheader.getStringContent();
System.out.println("RelatesTo message id: " + relatesToMessageId);

System.out.println("payload: '" + payload + "'");
System.out.println("RelatesTo: '" + RelatesTo + "'");
// Add your implementation here.
}
}

· Revised SimpleAsyncHelloCallbackImpl


package com.oracle.xmlns.simpleasynchello;

import com.sun.xml.ws.api.addressing.AddressingVersion;
import com.sun.xml.ws.api.message.Header;
import com.sun.xml.ws.api.message.HeaderList;
import com.sun.xml.ws.developer.JAXWSProperties;

import javax.annotation.Resource;

import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.ParameterStyle;
import javax.jws.soap.SOAPBinding.Style;

import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.ws.Action;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.soap.Addressing;
// !THE CHANGES MADE TO THIS FILE WILL BE DESTROYED IF REGENERATED!
// This source file is generated by Oracle tools
// Contents may be subject to change
// For reporting problems, use the following
// Version = Oracle WebServices (11.1.1.0.0, build 080922.1045.35800)

@SOAPBinding(style=Style.DOCUMENT, parameterStyle=ParameterStyle.BARE)
@WebService(name = "SimpleAsyncHelloCallback", targetNamespace = "http://xmlns.oracle.com/SimpleAsyncHello", portName = "SimpleAsyncHelloCallbackPort")
public class SimpleAsyncHelloCallbackImpl
{
@Resource
private WebServiceContext wsContext;

private static final AddressingVersion WS_ADDR_VER = AddressingVersion.W3C;

@SOAPBinding(parameterStyle=ParameterStyle.BARE)
@Action(input="onResult")
@Oneway
@WebMethod(action = "onResult")
public void onResult(@WebParam(targetNamespace="http://xmlns.oracle.com/SimpleAsyncHello",
partName="payload", name="SimpleAsyncHelloProcessResponse")
com.oracle.xmlns.simpleasynchello.SimpleAsyncHelloProcessResponse payload,
@WebParam(targetNamespace="http://schemas.xmlsoap.org/ws/2003/03/addressing",
partName="RelatesTo", name="RelatesTo", header=true)
org.xmlsoap.schemas.ws._2003._03.addressing.Relationship RelatesTo)
{
// Use the sample code to extract the relatesTo id for correlation and then add your rest of the logic

System.out.println("Received the asynchronous reply");

// get the messageId to correlate this reply with the original request
HeaderList headerList = (HeaderList)wsContext.getMessageContext().get(JAXWSProperties.INBOUND_HEADER_LIST_PROPERTY);
Header relatesToheader = headerList.get(WS_ADDR_VER.relatesToTag, true);

if (relatesToheader == null){
System.out.println("Header null");
}
// String relatesToMessageId = relatesToheader.getStringContent();
// System.out.println("RelatesTo message id: " + relatesToMessageId);

System.out.println("payload: '" + payload + "'" + payload.getResult());
System.out.println("RelatesTo: '" + RelatesTo + "'");
// Add your implementation here.
}
}

· Test and review output in the WLS console tab in JDEV

Monday, February 23, 2009

BPEL - Embedded Java - JAXB

A simple how-to on leveraging JAXB within your BPEL process - with help from my colleague Flavius!

Step 1 - create a project in JDev to hold the JAXB artifacts for your XSD e.g. order.xsd
Menu --> Tools--> JAXB Compilation

Jar up the classes e.g. orderjaxb.jar

Step 2 - create the BPEL project e.g. with input/output set to order from order.xsd
Add an Assign to copy input to output
After the Assign, add the Java embedding with the following code -

addAuditTrailEntry("Java Embedding JAXB");
try{

// get the output Order element
Element element = (Element)getVariableData("outputVariable", "payload", "/ns1:order");
// Create the JAXB Context for Order
JAXBContext jc = JAXBContext.newInstance("orderjaxb");
Unmarshaller u = jc.createUnmarshaller();
// Cast the output order element as an object of class Order
Order order = (Order)u.unmarshal((org.w3c.dom.Node)element);

// Display customer
addAuditTrailEntry("Order: " + order.getCustomer());
}



catch (Exception e){
addAuditTrailEntry("Java embedding Exception thrown...." + e.toString());
}

Add the imports -





Add the following 2 libraries to your BPEL project (via project properties)
Oracle XMLParser V2
orderJaxb (Create a new library entry pointing to the jar file created in step 1)

That's it!

Thursday, January 29, 2009

Recovering BPEL processes Part 2

The previous post was rather basic, but gives the general idea of what is to be done.
I've augmented the code, using an example from my colleagues (Thanks Silviu!) , as follows -

externalised connect info etc. in a properties file
specified the process ID and time constraints in the WHERE clause


The properties file is as follows -

#orabpel.platform=ias_10g
#java.naming.provider.url=opmn:ormi://localhost:6003:home/orabpel

# JNDI Properties required for Web Client

orabpel.platform=oc4j_10g
java.naming.provider.url=ormi://localhost:6010/orabpel
java.naming.factory.initial=oracle.j2ee.rmi.RMIInitialContextFactory
java.naming.security.principal=oc4jadmin
java.naming.security.credentials=welcome1


# JNDI Properties required for Remote Client

#orabpel.platform=oc4j_10g
#java.naming.provider.url=opmn:ormi://ncommisk-de:6010:oc4j_soa/orabpel
#java.naming.factory.initial=oracle.j2ee.rmi.RMIInitialContextFactory
#java.naming.security.principal=oc4jadmin
#java.naming.security.credentials=welcome1

# how many bpel domains must be recovered (usually just one)
domains.number=1
# ID of the bpel domain to be recovered
domain.1.ID=default
#domain.2.ID=oc4j_soa
#domain.3.ID=mydomain

# password of the bpel domain to be recovered
domain.1.password=welcome1
#domain.2.password=oc4j_soa
#domain.3.password=mydomain

# ID of the Process to be recovered
processID=AIADemoOrderEBF
#
# throttling - number of messages to be recovered in batch mode
# if -1, then all the recoverable messages will be scheduled in a single batch
#batch.size = 10
# time between 2 consecutive batch recovering, in milli seconds
#batch.frequency = 5000
#

# can be one of the 4: second, minute, hour or day (case insensitive)
# default is second
message.age.type=second
# message age in order to be candidate for manual recovering.
# the significance of the number is given by message.age.type property
message.age = 5
# if true, it will display the summary about all the messages (callback, invoke, activity) in bpel engine.
# for debugging purpose only, not for production mode
verbose = true
# if true, does not actually recover any message. Default is false
simulate = true

The Recovery class is as follows -

package com.oracle;


import com.oracle.bpel.client.*;

import com.oracle.bpel.client.util.SQLDefs;
import com.oracle.bpel.client.util.WhereCondition;

import com.oracle.bpel.client.util.WhereConditionHelper;

import java.io.File;

import java.net.URL;

import java.sql.Date;

import java.util.ArrayList;
import java.util.List;

import java.util.Properties;

public class Recovery{

private static final int MSG_AGE_TYPE_SECOND = 0;
private static final int MSG_AGE_TYPE_MINUTE = 1;
private static final int MSG_AGE_TYPE_HOUR = 2;
private static final int MSG_AGE_TYPE_DAY = 3;

/* Default: Recover instances older than n seconds
can be overwritten in properties file (message.age.type) */
private int msgAgeType = MSG_AGE_TYPE_SECOND;
private Date receiveDate;
private int msgAge = 5;
/* By default we will recover for all processIDs
can be overwritten in properties file (processID)*/
private String recoveryProcessID = "ALL";

private Properties props = new Properties();

/* Nr of Domains to be checked. Default is 1 (default domain)*/

private int NrOfDomains = 1;

/*configurable via the following in the properties file
domains.number=2, domain.1.ID=default, domain.2.ID=oc4j_soa domain.1.password=welcome1
domain.2.password=willkommen. Recover for domains default and oc4j_soa */

/* if true,will output contents of the properties file*/
private boolean verbose = true;
/* if true, does not actually recover any message*/
private boolean simulate = false;

private String[] domainIDs;
private String[] domainPasswords;
private Locator locator;

public Recovery() {
this(new Properties());
}

public Recovery(Properties props) {
if (props != null) {
this.props = props;
} else {
this.props = new Properties();
}
retrieveProperties();
}


public String doRecover() throws Exception{

// Read in the properties file
Properties props = new Properties();
URL url = new File("context.properties").toURL();
props.load(url.openStream());
System.out.println("props " + props.toString());
Recovery recover = new Recovery(props);

//
// Do the recovery for all of the specified domains
//
for (int i = 0; i < NrOfDomains; i++) {
try {
locator = new Locator(domainIDs[i], domainPasswords[i], props);
recoverInvoke();
recoverCallback();

} catch (ServerException e) {
System.out.println("AutomaticMsgRecovery: An error occured while recovering messages for domain: " +
domainIDs[i]);
e.printStackTrace();
}
}
return "*** Recovery Completed ***";
}

private void recoverCallbackMessages(List messages)
throws Exception
/* recoverCallbackMessages(): recover all Callback Messages*/
{
String messageGuids[] = new String[messages.size()];
for(int i = 0; i < messages.size(); i++)
{
ICallbackMetaData callbackMetadata = (ICallbackMetaData)messages.get(i);
String messageGuid = callbackMetadata.getMessageGUID();

messageGuids[i] = messageGuid;
System.out.println((new StringBuilder()).append("recovering callback message = ").append(messageGuids[i]).append(" process [").append(callbackMetadata.getProcessId()).append("(").append(callbackMetadata.getRevisionTag()).append(")] domain [").append(callbackMetadata.getDomainId()).append("]").toString());
}
if (!simulate){
IBPELDomainHandle domainHandle = locator.lookupDomain();
domainHandle.recoverCallbackMessages(messageGuids);
}
}

private void recoverInvokeMessages(List messages)
throws Exception
{
String messageGuids[] = new String[messages.size()];
for(int i = 0; i < messages.size(); i++)
{
IInvokeMetaData invokeMetadata = (IInvokeMetaData)messages.get(i);
String messageGuid = invokeMetadata.getMessageGUID();

messageGuids[i] = messageGuid;
System.out.println((new StringBuilder()).append("recovering invoke message = ").append(messageGuids[i]).append(" process [").append(invokeMetadata.getProcessId()).append("(").append(invokeMetadata.getRevisionTag()).append(")] domain [").append(invokeMetadata.getDomainId()).append("]").toString());

}
if (!simulate){
IBPELDomainHandle domainHandle = locator.lookupDomain();
domainHandle.recoverInvokeMessages(messageGuids);
}
}


private void recoverCallback() throws Exception{
//
// look for Callback messages in need of recovery
//
StringBuffer buf = new StringBuffer();
WhereCondition wc = WhereConditionHelper.whereCallbackMessagesRecoverable(); //
wc.append("AND");
// add older than date/time condition
wc.append(getWhereConditionWithTimestamp(SQLDefs.IM_receive_date));
wc.appendOrderBy(SQLDefs.IM_receive_date);

// Add processID to the where clause, if specified
if (!recoveryProcessID.equalsIgnoreCase("ALL")){
System.out.println("recoverCallback() only include instances for Process ID: " + recoveryProcessID );
buf.setLength( 0 );
WhereCondition tmpWhere = new WhereCondition( buf.append( " AND " )
.append( SQLDefs.AL_ci_process_id )
.append( " = ? " )
.toString() );
tmpWhere.setString( 1, recoveryProcessID );
wc.append( tmpWhere );
System.out.println("recoverCallback() looking for Callback instances where: "+ wc.getClause() );
System.out.println("process_id = "+ recoveryProcessID);
System.out.println("receive_date = "+ receiveDate.toString() + " " + receiveDate.getTime());

}
try{
ICallbackMetaData imd[] = locator.listCallbackMessages(wc);
List l = new ArrayList();
for (Object o:imd){
l.add(o);
}
// See how many Callbacks are in the recovery zone
System.out.println("recoverCallback() number of Callback instances to be recovered: " +l.size());
if (l.size()>0){
recoverCallbackMessages(l);
}

}
catch (Exception e){
e.printStackTrace();
}
}


public String recoverInvoke() throws Exception{
try{

// look for Invoke messages in need of recovery
StringBuffer buf = new StringBuffer();

//
WhereCondition wc = WhereConditionHelper.whereInvokeMessagesRecoverable();
wc.append("AND");
// add older than date/time condition

wc.append(getWhereConditionWithTimestamp(SQLDefs.IM_receive_date));
wc.appendOrderBy(SQLDefs.IM_receive_date);

// Add processID to the where clause, if specified
if (!recoveryProcessID.equalsIgnoreCase("ALL")){
System.out.println("recoverInvoke() only include instances for Process ID: " + recoveryProcessID );
buf.setLength( 0 );
WhereCondition tmpWhere = new WhereCondition( buf.append( "AND " )
.append( SQLDefs.AL_ci_process_id )
.append( " = ? " )
.toString() );
tmpWhere.setString( 1, recoveryProcessID );
wc.append( tmpWhere );
}

System.out.println("recoverInvoke() looking for Invoke instances where: "+ wc.getClause() );
System.out.println("process_id = "+ recoveryProcessID);
System.out.println("receive_date = "+ receiveDate.toString() + " " + receiveDate.getTime());

IInvokeMetaData imd1[] = locator.listInvokeMessages(wc);

// iterate thru the list

List l = new ArrayList();
for (Object o:imd1){
l.add(o);
}
// See how many INVOKES are in the recovery zone
System.out.println("recoverInvoke() number of Invoke instances to be recovered: " +l.size());

if (l.size()>0){
recoverInvokeMessages(l);
}
}
catch (Exception e){
e.printStackTrace();
}
return "done";
}


/*public Locator getLocator(){
System.out.println("getLocator() start");
Locator locator = null;

// set JNDI properties for BPEL lookup
String jndiProviderUrl = "opmn:ormi://ncommisk-de:6010:oc4j_soa/orabpel";
String jndiFactory = "com.evermind.server.rmi.RMIInitialContextFactory";
String jndiUsername = "oc4jadmin";
String jndiPassword = "welcome1";

Hashtable jndi = new Hashtable();
jndi.put(Context.PROVIDER_URL, jndiProviderUrl);
jndi.put(Context.INITIAL_CONTEXT_FACTORY, jndiFactory);
jndi.put(Context.SECURITY_PRINCIPAL, jndiUsername);
jndi.put(Context.SECURITY_CREDENTIALS, jndiPassword);
jndi.put("dedicated.connection", "true");

try{
System.out.println("getLocator() instantiating locator...");
locator = new Locator("default", "bpel", jndi);
System.out.println("getLocator() instantiated locator");
}
catch (Exception e){
System.out.println("getLocator() error");
e.printStackTrace();
}

return locator;
}
*/
private WhereCondition getWhereConditionWithTimestamp(String dateColumn) {
/* Only pick up invoke messages that are older than x ...
to avoid picking up the messages
with state = 0, which are in process, but their state was not yet saved in DB
Default is x, depending on following values specified in properties file
e.g. message.age = 5 and message.age.type=second = 5 seconds
e.g. message.age = 3 and message.age.type=minute = 3 minutes
*/
WhereCondition wc = new WhereCondition();

wc.append(dateColumn + " < ? ");

// Assuming seconds
long millis = msgAge * 1000;

if (msgAgeType == MSG_AGE_TYPE_MINUTE) {
// minute type
millis = millis * 60;
} else if (msgAgeType == MSG_AGE_TYPE_HOUR) {
// hour type
millis = millis * 60 * 60;
}
else if (msgAgeType == MSG_AGE_TYPE_DAY) {
// day type
millis = millis * 24 * 60 * 60;
}
receiveDate = new Date(System.currentTimeMillis() - millis);
wc.setTimestamp(1, receiveDate);
System.out.println(" *** getWhereConditionWithTimestamp(): looking for process instances older than " + millis/1000 + " seconds ");
return wc;
}

private void retrieveProperties() {


// Recover only for a particular process ID?
try {
recoveryProcessID = props.getProperty("processID", "ALL");
}
catch (Exception e) {
;
}

try {
String temp = props.getProperty("message.age.type", "second");
if ("day".equalsIgnoreCase(temp))
this.msgAgeType = MSG_AGE_TYPE_DAY;
else if ("minute".equalsIgnoreCase(temp))
this.msgAgeType = MSG_AGE_TYPE_MINUTE;
else if ("hour".equalsIgnoreCase(temp))
this.msgAgeType = MSG_AGE_TYPE_HOUR;
else // default
this.msgAgeType = MSG_AGE_TYPE_SECOND;

} catch (Exception e) {
;
}

try {
int temp = Integer.parseInt(props.getProperty("message.age", "5"));
if (temp >= 0)
this.msgAge = temp;
} catch (Exception e) {
;
}


this.verbose = Boolean.parseBoolean(props.getProperty("verbose", "false"));


try {
int temp = Integer.parseInt(props.getProperty("domains.number", "1"));
if (temp >= 1)
this.NrOfDomains = temp;
} catch (Exception e) {
;
}

this.domainIDs = new String[NrOfDomains];
this.domainPasswords = new String[NrOfDomains];

for (int i = 1; i <= NrOfDomains; i++) {
domainIDs[i - 1] = props.getProperty("domain." + i + ".ID", "default");
domainPasswords[i - 1] = props.getProperty("domain." + i + ".password");
}

this.simulate = Boolean.parseBoolean(props.getProperty("simulate", "false"));

if (verbose) {
printProperties();
}

} // private void retrieveProperties()


private void printProperties () {
System.out.println("Properties used by: " + this.getClass().getName());
System.out.println("##############################################");
System.out.println("orabpel.platform: " + props.getProperty("orabpel.platform"));
System.out.println("java.naming.provider.url: " + props.getProperty("java.naming.provider.url"));
System.out.println("java.naming.factory.initial: " + props.getProperty("java.naming.factory.initial"));
System.out.println("java.naming.security.principal: " + props.getProperty("java.naming.security.principal"));
System.out.println("java.naming.security.credentials: " + props.getProperty("java.naming.security.credentials"));

for (int i = 1; i <= NrOfDomains; i++) {
System.out.print("domain." + i + ".ID: " + domainIDs[i - 1]);
System.out.println(" \tdomain." + i + ".password: " + domainPasswords[i - 1]);
}

System.out.print("message.age.type: ");
if (msgAgeType == MSG_AGE_TYPE_MINUTE) {
System.out.println("minute");
} else if (msgAgeType == MSG_AGE_TYPE_HOUR) {
System.out.println("hour");
}
else if (msgAgeType == MSG_AGE_TYPE_DAY) {
System.out.println("day");
}else {
System.out.println("second");
}

System.out.println("message.age: " + msgAge);
System.out.println("process ID" + recoveryProcessID);
System.out.println("verbose: " + verbose);
System.out.println("simulate: " + simulate);
System.out.println();

}

}

In my scenario this needs to run as a daemon, so the idea is to call this class from
a Servlet (init method)

public void init(ServletConfig config) throws ServletException {
super.init(config);

boolean loop = true;
try{
Recovery rec = new Recovery();

while (loop){
rec.doRecover();
// sleep for 5 seconds
Thread.currentThread().sleep(5000);
}
}
catch (Exception e){
e.printStackTrace();
}

}

Tuesday, January 27, 2009

BPEL process recovery using the BPEL API

The code below is an example of using the BPEL API from a remote java client, to
recover messages.

include the following libraries -
oc4j-internal.jar
optic.jar
orabpel.jar
orabpel-common.jar
(thanks to http://oraclebpelindepth.blogspot.com/2008/09/jars-for-bpel-java-api.html for this)

package bpelrecovery;

import com.oracle.bpel.client.*;

import com.oracle.bpel.client.util.SQLDefs;
import com.oracle.bpel.client.util.WhereCondition;

import java.util.ArrayList;
import java.util.Hashtable;

import java.util.List;

import javax.naming.Context;


public class Recover {
public Recover() {
}

public static void main(String[] args) {
Recover recover = new Recover();
String rtc = "";

try{
rtc = recover.doRecover();
}
catch (Exception e){
e.printStackTrace();
}

}


private void recoverCallbackMessages(List messages)
throws Exception
{
String messageGuids[] = new String[messages.size()];
for(int i = 0; i < messages.size(); i++)
{
ICallbackMetaData callbackMetadata = (ICallbackMetaData)messages.get(i);
String messageGuid = callbackMetadata.getMessageGUID();
messageGuids[i] = messageGuid;
System.err.println((new StringBuilder()).append("recovering callback message = ").append(messageGuids[i]).append(" process [").append(callbackMetadata.getProcessId()).append("(").append(callbackMetadata.getRevisionTag()).append(")] domain [").append(callbackMetadata.getDomainId()).append("]").toString());
}
Locator locator = getLocator();
IBPELDomainHandle domainHandle = locator.lookupDomain();
domainHandle.recoverCallbackMessages(messageGuids);
}


public String doRecover() throws Exception{

// Connect to domain "default"
try{
System.out.println("doRecover() instantiating locator...");
Locator locator = getLocator();
System.out.println("doRecover() instantiated locator for domain " + locator.lookupDomain().getDomainId());

// look for Invoke messages in need of recovery
StringBuffer buf1 = new StringBuffer();
WhereCondition where = new WhereCondition(buf1.append(SQLDefs.IM_state).append( " = " ).append(IDeliveryConstants.STATE_UNRESOLVED ).toString() );
System.out.println("doRecover() instantiating IInvokeMetaData... with where = "+ where.getClause());
IInvokeMetaData imd1[] = locator.listInvokeMessages(where);
System.out.println("doRecover() instantiated IInvokeMetaData");

// iterate thru the list

List l1 = new ArrayList();
for (Object o:imd1){
l1.add(o);
}
// See how many INVOKES are in the recovery zone
System.out.println("doRecover() instantiated IInvokeMetaData size = " +l1.size());

// look for Callback messages in need of recovery
StringBuffer buf = new StringBuffer();
where = new WhereCondition(buf.append(SQLDefs.DM_state).append( " = " ).append(IDeliveryConstants.TYPE_callback_soap ).toString() );
System.out.println("doRecover() instantiating ICallbackMetaData... with where = "+ where.getClause());
ICallbackMetaData imd[] = locator.listCallbackMessages(where);
System.out.println("doRecover() instantiated ICallbackMetaData");

//
// recover
//
List l = new ArrayList();
for (Object o:imd){
l.add(o);
}
// recoverCallbackMessages(l);

}

catch (Exception e){
e.printStackTrace();
}
return "done";
}

public Locator getLocator(){
System.out.println("getLocator() start");
Locator locator = null;

// set JNDI properties for BPEL lookup
String jndiProviderUrl = "opmn:ormi://ncommisk-de:6010:oc4j_soa/orabpel";
String jndiFactory = "com.evermind.server.rmi.RMIInitialContextFactory";
String jndiUsername = "oc4jadmin";
String jndiPassword = "welcome1";

Hashtable jndi = new Hashtable();
jndi.put(Context.PROVIDER_URL, jndiProviderUrl);
jndi.put(Context.INITIAL_CONTEXT_FACTORY, jndiFactory);
jndi.put(Context.SECURITY_PRINCIPAL, jndiUsername);
jndi.put(Context.SECURITY_CREDENTIALS, jndiPassword);
jndi.put("dedicated.connection", "true");

try{
System.out.println("getLocator() instantiating locator...");
locator = new Locator("default", "bpel", jndi);
System.out.println("getLocator() instantiated locator");
}
catch (Exception e){
System.out.println("getLocator() error");
e.printStackTrace();
}

return locator;
}

}

Monday, January 19, 2009

EJB 3.0 – EJB calling EJB (co-located) using local

EJB 3.0 – EJB calling EJB same package using local

Scenario: Session EJB1 calls Session EJB2 via local interface

Session EJB1

package project1;

import java.util.Hashtable;

import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;

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

@Stateless(name = "SessionEJB1", mappedName = "MyEJB-SessionEJB1")
@Remote
@Local
public class SessionEJB1Bean implements SessionEJB1, SessionEJB1Local {
public SessionEJB1Bean() {
}

public String sayHi(){
return "Hi";
}

public String callEJB2(){
try {
final Context context = getInitialContext();
SessionEJB2Local sessionEJB2 = (SessionEJB2Local)context.lookup("java:comp/env/App1EAR/SessionEJB2/local");
} catch (Exception ex) {
ex.printStackTrace();
}
return "called";


}
private static Context getInitialContext() throws NamingException {
Hashtable env = new Hashtable();
// WebLogic Server 10.x connection details
env.put( Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory" );
env.put(Context.PROVIDER_URL, "t3://localhost:7001");
return new InitialContext( env );
}
}

Session EJB2

package project1;

import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;

@Stateless(name = "SessionEJB2", mappedName = "MyEJB-SessionEJB2")
@Remote
@Local
public class SessionEJB2Bean implements SessionEJB2, SessionEJB2Local {
public SessionEJB2Bean() {


}
public String sayHello(){
return "Hello";
}
}

ejb-jar.xml



weblogic-ejb-jar.xml



EJB1 Test Client

package project1;

import java.util.Hashtable;

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

public class SessionEJB1Client {
public static void main(String [] args) {
try {
final Context context = getInitialContext();
SessionEJB1 sessionEJB1 = (SessionEJB1)context.lookup("App1EAR/SessionEJB1/remote");
System.out.println(sessionEJB1.sayHi());
System.out.println(sessionEJB1.callEJB2());
} catch (Exception ex) {
ex.printStackTrace();
}
}

private static Context getInitialContext() throws NamingException {
Hashtable env = new Hashtable();
// WebLogic Server 10.x connection details
env.put( Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory" );
env.put(Context.PROVIDER_URL, "t3://localhost:7001");
return new InitialContext( env );
}
}