Advertisements
RSS

Oracle Service Bus logging & tracing III – Creating a Custom Reporting Provider

15 Feb

In my earlier blogpost explaining the Oracle Service Bus Report Action I already mentioned the fact that Oracle allows you to create a custom report provider. Quote:

If you do not wish to use the JMS Reporting Provider that is provided with your Oracle Service Bus installation, you can untarget it and create your own reporting provider using the Reporting Service Provider Interface (SPI). If you configure your own reporting provider for messages, no information is displayed in the Oracle Service Bus Administration Console. You must to create your own user interface.

Since the report action places a java object on an internal JMS queue named wli.reporting.jmsprovider.queue we can play around with it from their.

Initially we wanted to discover what information this Java Object contained. So I’ve created a simple EJB project as an example named it CustomOsbReportHandler.


package local.rubix;
import java.util.logging.Logger;

import javax.annotation.Resource;
import javax.ejb.MessageDriven;
import javax.ejb.MessageDrivenContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.ObjectMessage;
import javax.jms.TextMessage;

import weblogic.logging.LoggingHelper;

import com.bea.wli.reporting.jmsprovider.runtime.ReportMessage;

@MessageDriven(mappedName = "wli.reporting.jmsprovider.queue",
 name = "CustomReportHandler")

public class CustomOsbReportHandler implements MessageListener
{

 @Resource private MessageDrivenContext mdc;

 public void onMessage(Message inMessage)
 {
 TextMessage msg = null;
 Logger logger = LoggingHelper.getServerLogger();
 logger.warning("=====================");
 logger.warning("MDB inMessage: " + inMessage);
 try
 {
 if (inMessage instanceof TextMessage)
 {
 msg = (TextMessage) inMessage;
 logger.info("MDB unexpected text message received: " + msg.getText());
 }
 else if (inMessage instanceof ObjectMessage)
 {
 ObjectMessage myObject = (ObjectMessage) inMessage;
 logger.warning("MDB ObjectClass: " + myObject.getObject().getClass());
 String str_inMessageClassName = myObject.getObject().getClass().getSimpleName();
 logger.warning("MDB ObjectName: " + str_inMessageClassName);
 if (str_inMessageClassName.equals("ReportMessage"))
 {
 logger.warning("MDB ReportMessage found");
 ReportMessage myReportMessage = (ReportMessage)myObject.getObject();
 logger.warning("MDB getMetadata: " + myReportMessage.getMetadata());
 logger.warning("MDB getStrPayload: " + myReportMessage.getStrPayload());
 logger.warning("MDB getXmlPayload: " + myReportMessage.getXmlPayload());
 logger.warning("MDB getBinPayload: " + myReportMessage.getBinPayload());
 }
 else
 {
 logger.warning("MDB NO ReportMessage");
 }
 }
 else
 {
 logger.info("MDB unknown message");
 }
 logger.warning("=====================");
 }
 catch (JMSException e)
 {
 e.printStackTrace();
 mdc.setRollbackOnly();
 }
 catch (Throwable te)
 {
 te.printStackTrace();
 }
 }

}

This project was build with WLS/OSB 11.1.1.5 and the project uses the following libs:

  • com.bea.alsb.reporting.impl.jar
  • com.bea.core.xml.xmlbeans_2.1.0.0_2-5-1.jar
  • org.eclipse.persistence_1.1.0.0_2-1.jar
When you deploy your project don’t forget to untarget the default JMS Reporting Provider in the Weblogic console.

Output:

#### .... <BEA-000000> <=====================>
#### .... <BEA-000000> <MDB inMessage: ObjectMessage[ID:<786041.1329324233629.0>,com.bea.wli.reporting.jmsprovider.runtime.ReportMessage@9f0e4f]>
#### .... <BEA-000000> <MDB ObjectClass: class com.bea.wli.reporting.jmsprovider.runtime.ReportMessage>
#### .... <BEA-000000> <MDB ObjectName: ReportMessage>
#### .... <BEA-000000> <MDB ReportMessage found>
#### .... <BEA-000000> <MDB getMetadata: <rep:messagecontext xmlns:rep="http://www.bea.com/wli/reporting">
 <rep:content-encoding>UTF-8</rep:content-encoding>
 <rep:labels>CorrelationID=1234567890</rep:labels>
 <rep:inbound-endpoint name="ProxyService$LocalTest$services$ProxyService">
 <rep:service>
 <rep:operation>getEmployeeDetails</rep:operation>
 </rep:service>
 <rep:transport>
 <rep:uri>/service/employeedetails/v1</rep:uri>
 <rep:mode>request-response</rep:mode>
 <rep:qualityOfService>best-effort</rep:qualityOfService>
 </rep:transport>
 </rep:inbound-endpoint>
 <rep:origin>
 <rep:state>REQUEST</rep:state>
 <rep:node>PipelinePairNode1</rep:node>
 <rep:pipeline>PipelinePairNode1_request</rep:pipeline>
 <rep:stage>stageValidate</rep:stage>
 </rep:origin>
 <rep:timestamp>2012-01-11T16:20:08.874+01:00</rep:timestamp>
</rep:messagecontext>>
 <BEA-000000> <MDB getStrPayload: null>
#### .... <BEA-000000> <MDB getXmlPayload: <head:myHeader xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:sr01="http://rubix.nl/schemas/cdm/sr01" xmlns:head="http://rubix.nl/schemas/cdm/header">
 <head:CurrentDateTime>2012-01-11T14:44:29.857Z</head:CurrentDateTime>
 <head:CorrelationID>1234567890</head:CorrelationID>
 <head:MessageID>1234567890.1</head:MessageID>
</head:myHeader>>
#### .... <BEA-000000> <MDB getBinPayload: null>
#### .... <BEA-000000> <=====================>

Which shows us that:

As expected the getXmlPayload & getBinPayload return either the expression you selected in the report action or null. Depending on what kind of input data you decided to use in your mapping. Much more interesting is the getMetadata method which returns the message reporting context, which is an XML type defined in the Oracle Service Bus MessageReporting.xsd. The coolest part here is the fact that it always contains the origin segment with variables you normally don’t have at your disposal in your OEPE mapper. This origin segment contains information as the state (REQUEST/RESPONSE/ERROR), node, pipeline and stage of the running process.

<rep:origin>
  <rep:state>REQUEST</rep:state>
  <rep:node>PipelinePairNode1</rep:node>
  <rep:pipeline>PipelinePairNode1_request</rep:pipeline>
  <rep:stage>stageValidate</rep:stage>
</rep:origin>

So now what ?

With the help of this information we started thinking about a proper database model to store the information needed for the support team. Now it was key to create a proper Reporting Data Handler which could replace the default JMS Reporting Provider.

The following code is based on the BEA example named SAMPLE-REPORTPROVIDER which can be downloaded here.

First the ApplicationLifecycleListener which registers our own ReportingHandler:

package nl.rubix;
import java.util.logging.Logger;

import weblogic.application.ApplicationLifecycleEvent;
import weblogic.application.ApplicationLifecycleListener;
import weblogic.logging.LoggingHelper;

import com.bea.wli.reporting.ReportingDataHandler;
import com.bea.wli.reporting.ReportingDataManager;

public class CustomOsbApplicationLifecycleListener extends ApplicationLifecycleListener
{

Logger logger = LoggingHelper.getServerLogger();

@Override
 public void postStart(ApplicationLifecycleEvent evt)
 {
 this.logger.warning("POST START");
 ReportingDataManager rmgr = ReportingDataManager.getManager();
 CustomOsbReportingDataHandler myHandler = new CustomOsbReportingDataHandler();
 ReportingDataHandler[] handlers = rmgr.getHandlers();
 myHandler.setHandlers(handlers);

// Remove the already installed handlers
 for (ReportingDataHandler h: handlers)
 {
 rmgr.removeHandler(h);
 }
 rmgr.addHandler(myHandler);
 this.logger.warning("POST START - add handler: " + myHandler.toString());
 }

 @SuppressWarnings("null")
 @Override
 public void preStop(ApplicationLifecycleEvent evt)
 {
 this.logger.warning("PRE STOP");
 ReportingDataManager rmgr = ReportingDataManager.getManager();
 ReportingDataHandler[] handlers = rmgr.getHandlers();
 CustomOsbReportingDataHandler myHandler = null;
 // Remove the already installed handlers
 for (ReportingDataHandler h: handlers)
 {
 if (h instanceof CustomOsbReportingDataHandler)
 {
 myHandler = (CustomOsbReportingDataHandler)h;
 }
 rmgr.removeHandler(h);
 }

handlers = myHandler.getHandlers();
 if (handlers != null)
 {
 // Install the original handlers back
 for (ReportingDataHandler h: handlers)
 {
 rmgr.addHandler(h);
 }
 }
 }
}

Second our own ReportDataHandler:

package nl.rubix;
import java.io.InputStream;
import java.io.Serializable;
import java.util.logging.Logger;

import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlTokenSource;

import weblogic.logging.LoggingHelper;

import com.bea.wli.reporting.MessagecontextDocument;
import com.bea.wli.reporting.ReportingDataHandler;

public class CustomOsbReportingDataHandler implements ReportingDataHandler
{
 Logger logger = LoggingHelper.getServerLogger();

public CustomOsbReportingDataHandler()
 {
 // i know nothing ...
 }

 @Override
 public void close()
 {
 // TODO Auto-generated method stub
 this.logger.warning("CLOSE");
 }

@Override
 public void flush() {
 // TODO Auto-generated method stub
 this.logger.warning("FLUSH");
 }

@Override
 public void handle(XmlObject metadata, String str) throws Exception {
 // Handling OSB Alert/Report expression mapping containing String data
 this.logger.warning("handle XMLObject/String");
 }

@Override
 public void handle(XmlObject metadata, InputStream is) throws Exception {
 // Handling OSB Alert/Report expression mapping containing InputStream data
 this.logger.warning("handle XML/InputStream");
 is.mark(0);
 is.reset();
 }

@Override
 public void handle(XmlObject metadata, XmlObject data) throws Exception {
 // Handling OSB Alert/Report expression mapping containing XML data (expected behaviour)
 this.logger.warning("handle XMLObject/XMLObject");
 handleReportXMLdata( metadata, data);
 }

@Override
 public void handle(XmlObject arg0, XmlTokenSource arg1) throws Exception {
 // Handling OSB Alert/Report expression mapping containing XMLTokenSource data
 this.logger.warning("handle XMLObject/XmlTokenSource");
 }

@Override
 public void handle(XmlObject arg0, Serializable arg1) throws Exception {
 // Handling OSB Alert/Report expression mapping containing Serializable data
 this.logger.warning("handle XMLObject/Serializable");
 }

public void setHandlers(ReportingDataHandler[] handlers) {
 // TODO Auto-generated method stub
 this.logger.warning("setHandlers");
 }

public ReportingDataHandler[] getHandlers()
 {
 // TODO Auto-generated method stub
 this.logger.warning("getHandler");
 return null;
 }

 private void handleReportXMLdata(XmlObject metadata, XmlObject data)
 {
 this.logger.warning("handleReportData kicked in");
 try
 {
 // check if Report message
 if( metadata instanceof MessagecontextDocument )
 {
 this.logger.warning("ReportMetadata = " + metadata.xmlText());
 this.logger.warning("ReportData = " + data.xmlText());
 }
 /* else if( metadata instanceof AlertcontextDocument )
 {
 this.logger.warning("AlertMetadata = " + metadata.xmlText());
 }*/
 }
 catch(Exception e)
 {
 e.printStackTrace();
 }
 }

}

During startup of our Weblogic server we can see in the logging the handler is registered.

<Warning> <> <BEA-000000> <POST START>
<Warning> <> <BEA-000000> <setHandlers>
<Warning> <> <BEA-000000> <POST START - add handler: nl.rubix.CustomOsbReportingDataHandler@396c77>

When triggering a Proxy Service which contains a Report action where $header is mapped in expression and $header/correlationID is mapped in a Key we see the following output:

<Warning> <> <BEA-000000> <handle XMLObject/XMLObject>
<Warning> <> <BEA-000000> <handleReportData kicked in>
<Warning> <> <BEA-000000> <ReportMetadata = <rep:messagecontext xmlns:rep="http://www.bea.com/wli/reporting">..............</rep:messagecontext>>
<Warning> <> <BEA-000000> <ReportData = <head:myHeader xmlns:head="http://rubix.nl/schemas/cdm/header">..............</head:myHeader>>

Why … 

From here you can decide on multiple solutions like storing the messagecontext and/or expression in a Oracle database which is the most obvious solution. By this creating your own audit & tracing solution for your organization needs.

Two of the biggest problems I have with the default reporting provider is the fact the messagecontext contains interesting data as a full timestamp but the record is stored containing only the message date. Besides that the default version dumps the full expression in a BLOB field. Both result in a limitation regarding querying for specific events.

Report actions + Custom Report provider + a custom SOAP Header definition + proper correlation through your services + good database model = very nice audit and tracing capabilities.

**updates**
2013-06-13 – added blogpost reference Edwin Biemond with detailed Java examples

References:

You can find more examples here:
Oracle Forums: https://forums.oracle.com/forums/thread.jspa?threadID=817677
Java.net: http://java.net/projects/oracleservicebus1031/downloads?page=2&theme=java.net
Edwin Biemond: Custom Reporting Provider

Advertisements
 
8 Comments

Posted by on 15-02-2012 in Oracle, OSB

 

Tags: , ,

8 responses to “Oracle Service Bus logging & tracing III – Creating a Custom Reporting Provider

  1. Farouk

    24-02-2012 at 00:49

    Hello,

    You said in your post : When you deploy your project don’t forget to untarget the default JMS Reporting Provider in the Weblogic console.

    But when i untargeted the JMS Reporting Provider, messages were not put anymore in the queue. So the EJB we deployed won’t be able to get the message from the queue.

    Did i miss something?

     
  2. jvzoggel

    24-02-2012 at 13:51

    Hi Farouk, this is correct. The JMS Reporting Provider acts as the ReportDataHandler and you need this for getting messages on the queue. My example only works to show the content of the Java Object message and was used in a proof-of-concept to convince the audience reporting could be used instead of numberous LogActions. Sorry for the confusion.

    What you really need is to implement your own ApplicationLifecycleListener that registers your own custom reportingprovider. Your own custom reportingprovider will need to implement ReportingDataHandler

    You can find more detailed examples here:
    https://forums.oracle.com/forums/thread.jspa?threadID=817677
    http://java.net/projects/oracleservicebus1031/downloads?page=2&theme=java.net (sample-reportprovider)

     
  3. jvzoggel

    07-03-2012 at 11:36

    Updated the blogpost to contain an example ReportingDataHandler and ApplicationLifecycleListener. In this example we expect the $expression mapping of the OSB Report Action to always contain XMLdata (since this is part of our Standards and Guidelines) so we haven’t implemented a binary or string input.

    hope it helps

     
    • Santhosh

      03-02-2013 at 21:36

      Hi jvzoggel, i am not able to import/find the “com.bea.wli.reporting.jmsprovider.runtime.ReportMessage” package, can you please tel me where we can get that package. Very urgent.

       
  4. archenroot

    14-01-2015 at 12:59

    Reblogged this on Black Hole Of My Memory.

     

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: