bea.com | products | dev2dev | support | askBEA

Download Docs | Site Map | Glossary
Search

Developing Adapters

Developing an Event Adapter

This section contains information about the following subjects:

- Introduction to Event Adapters

- Event Adapters in a Run-Time Environment

- Flow of Events

- Step 1: Define the Adapter

- Step 2: Configure the Development Environment

- Step 3: Implement the Adapter

- Step 4: Test the Adapter

- Step 5. Deploy the Adapter

Introduction to Event Adapters

Event adapters propagate information from an EIS to the WebLogic Integration environment; they can be described as publishers of information.

All WebLogic Integration event adapters perform the following three functions:

- They respond to events that occur inside the running EIS by extracting and storing data about the event from the EIS.

- They transform event data from an EIS-specific format to an XML document that conforms to the XML schema for the event. The XML schema is based on metadata in the EIS.

- They propagate the event in the WebLogic Integration environment by using the event router.

WebLogic Integration implements the aspects of these three functions that are common to all event adapters, allowing you to focus on only the EIS-specific aspects of your adapter.

Event Adapters in a Run-Time Environment

The behavior of an event in a run-time environment is depicted in Figure 7-1.

Figure 7-1 Event Adapters in a Run-time Environment

Flow of Events

Figure 7-2 outlines the steps required to develop an Event Adapter.

Figure 7-2 Event Adapter Flow of Events

Step 1: Define the Adapter

Before you start developing an event adapter, you must define your requirements for it. For a complete list of the information you need to do so, see Adapter Setup Worksheet. This section provides a summary of the most important tasks to be completed for step 1:

1. Define an event in terms of the following questions:

   - What will be the contents of the event?

   - How will the event be defined in the XML schema?

   - What will trigger the event?

2. Select one of the following data extraction methods:

   - Push—The EIS notifies the adapter of an event. Use this method when your adapter needs to poll the EIS to determine a change of state.

   - Pull—The adapter polls the EIS and pulls event data from it. Use this method when you want to implement event generation that works like a publish-and-subscribe model.

Step 2: Configure the Development Environment

This step involves completing a five-step procedure to prepare your computer for adapter development:

- Step 2a: Set Up the File Structure

- Step 2b: Assign a Logical Name to the Adapter

- Step 2c: Set Up the Build Process

- Step 2d: Create the Message Bundle

- Step 2e: Configure Logging

Step 2a: Set Up the File Structure

The file structure needed for an event adapter development environment is the same as that required for developing service adapters. For details, see Step 2a: Set Up the Directory Structure in Developing a Service Adapter.

Step 2b: Assign a Logical Name to the Adapter

Assign a logical name to your adapter. By convention, this name comprises the vendor name, the type of EIS connected to the adapter, and the version number of the EIS, and it is expressed as vendor_EIS-type_EIS version. For example:

BEA_WLS_SAMPLE_ADK

This name includes the following components:

- BEA_WLS is the vendor.

- SAMPLE is the EIS type.

- ADK is the EIS version.

Step 2c: Set Up the Build Process

WebLogic Integration employs a build process based on Ant, a 100% pure Java-based build tool. For more information about how Ant works, see Ant-Based Build Process. For more information about how to use Ant, go to:

>     http://jakarta.apache.org/ant/index.html

The sample adapter provided by WebLogic Integration contains an Ant build file: WLI_HOME/adapters/sample/project/build.xml. This file, in turn, contains the tasks needed to build a J2EE-compliant adapter. When you run the GenerateAdapterTemplate utility to clone a development tree for your adapter, a build.xml file is created specifically for that adapter. Because this file is generated automatically, you do not need to customize the sample build.xml file and you can be sure that the code is correct. For information about using the GenerateAdapterTemplate utility, see Creating a Custom Development Environment.

For more information about the build process, see Step 2c: Set Up the Build Process in Developing a Service Adapter.

Step 2d: Create the Message Bundle

Any message destined for an end-user should be placed in a message bundle: a .properties text file containing key=value pairs that allow you to internationalize messages. When a geographic locale and a natural language are specified for a message at run time, the contents of the message are interpreted on the basis of the key=value pair, and the message is presented to the user in the specified language.

For instructions on creating a message bundle, see the JavaSoft tutorial on internationalization at:

http://java.sun.com/docs/books/tutorial/i18n/index.html

Step 2e: Configure Logging

Logging is performed with a logging tool called Log4j, which was developed as part of the Apache Jakarta project.

Before you begin this step, we recommend that you read more about logging in Basic Development Concepts, and about how to use Log4j in Using the Logging Toolkit.

Create an Event Generation Logging Category

If you are planning to use an event adapter, you must create a logging category specifically for event generation. (For more information about logging categories, see Message Categories.) To edit the logging configuration file for a specific adapter (WLI_HOME/adapters/YOUR_ADAPTER/src/
adapter_logical_name.xml), add the code shown in the following listing.

Listing 7-1 Sample Code for Creating an Event Generation Logging Category

>     <category name='BEA_WLS_SAMPLE_ADK.EventGenerator' class='com.bea.
>        logging.LogCategory'>

>     </category>

Replace BEA_WLS_SAMPLE_ADK with the logical name of your adapter.

If you do not set any parameters for this category, it inherits all the property settings of the parent category. In this example, the parent category is BEA_WLS_SAMPLE_ADK. Although you are not required to use the adapter logical name as the root category, you must use a unique identifier so that there is no impact on other adapters in a multi-adapter enviroment.

Step 3: Implement the Adapter

To implement an event adapter, you must complete the following two-step procedure:

1. Create an event generator. This process implements the data extraction method (in either push or a pull mode) and the IEventGenerator interface. (The latter interface is used by the event router to drive the event generation process.) This step is described in Step 3a: Create an Event Generator.

2. Implement the data transformation method. This step is described in Step 3b: Implement the Data Transformation Method.

Step 3a: Create an Event Generator

Event generation provides an adapter with a mechanism to either receive notification from an EIS or poll an EIS for a specific occurrence of an event. The WebLogic Integration engine provides a powerful event generator that can support multiple types of events. An event type is defined by the configuration properties for an event.

Typically event properties are defined by the properties associated with an event at design time. When configuring an event adapter, keep in mind that you may designate one or more Web pages from which the adapter will collect event properties. These properties are saved with the application view descriptor and passed back to the event at run time. The WebLogic Integration engine uses the properties and the source application view to determine how to route back to the listeners. For instance, if two separate deployments of the same event generator with identical properties are used, only one IEventDefinition is created by the WebLogic Integration engine. If different properties are specified, however, a single IEventDefinition is created for each deployment of a single event adapter. The event generator must determine which IEventDefinition to use in the routing process. This determination is typically made on the basis of property values and specific event occurrences.

IEventDefinition objects are used by your implementation of the event generator to route specific events back to their listener. As discussed elsewhere, the WebLogic Integration engine creates IEventDefinition objects for deployed application views containing events. IEventDefinition objects can be used to extract specific properties associated with the deployment of an application view, or to access schema and routing objects. You must employ these attributes when routing an event.

How the Data Extraction Mechanism Is Implemented

WebLogic Integration supports two modes of data extraction:

- Push event generation—A state change is recognized when the object generating events pushes a notification to the event generator. When the push event generator receives the event the WebLogic Integration engine then routes the event to a deployed application view. The push event generator uses a publish-and-subscribe model.

- Pull event generation—Used when polling is required to confirm a state change. A process continually queries an object until it detects a change in state, at which point it creates an event, which the WebLogic Integration engine then routes to a deployed application view.

Pull Mode

Pull mode relies on a polling technique to determine whether an event has taken place. To implement it, you must derive your event generator from the AbstractPullEventGenerator in the com.bea.adapter.event package.

Note: The adk-eventgenerator.jar file contains the ADK base classes required to implement an event generator. It must be included in your WAR make file.

In the AbstractPullEventGenerator, the ADK supplies several abstract methods that you must override in your implementation. These methods are described in the following table.

Table 7-1 AbstractPullEventGenerator Methods

Method | Description
--- | ---
postEvents() | Control method for the remainder of your event generation, message transformation, and routing code; allows you to add polling and routing code. Called from the run method in the AbstractPullEventGenerator at an interval determined by the Event Router configuration files.
setupNewTypes() | Method for preprocessing any IEventDefinition object being deployed. Only valid new IEventDefinition objects can be passed to this method.
removeDeadTypes() | Handles any cleanup required for IEventDefinition objects being undeployed. The WebLogic Integration engine calls this method when application views with associated events are being undeployed.
doInit() | Method called while the event generator is being constructed. During the initialization process the event generator can use predefined configuration values to set up the necessary state or connections for the event generation process.
doCleanUpOnQuit() | Frees resources allocated by your event generation process. Called before the thread driving the event generation process is ended.

Push Mode

Push mode uses notification to trigger the routing of an event. To implement it, you must derive your event generator from the AbstractPushEventGenerator class in the com.bea.adapter.event package. Several other supporting classes are included in the event package. These classes are described in Table 7-2.

Note: The adk-eventgenerator.jar file contains the WebLogic Integration base classes required to implement an event generator. It must be included in your WAR make file.

Table 7-2 AbstractPushEventGenerator Classes

Class | Description
--- | ---
AbstractPushEventGenerator | Class containing the same abstract and concrete methods as the AbstractPullEventGenerator. The methods in both implementations (AbstractPullEventGenerator and AbstractPushEventGenerator) are intended to be used in the same manner. For a list of the methods and responsibilities associated with each, see Table 7-1.
IPushHandler | Interface provided primarily to abstract the generation of an event from the routing of an event. It is not required for the implementation of the push mode of data extraction. The IPushHandler is designed to be tightly coupled with the PushEventGenerator. The PushEventGenerator initializes, subscribes, and cleans up the PushHandler implementation. The IPushHandler provides a simple interface to abstract the generation logic. The interface provides methods to initialize, subscribe to push events, and clean up resources.
PushEvent | PushEvent is an event object derived from java.util.EventObject. The PushEvent object is designed as a wrapper for an EIS notification, which is sent to any IPushEventListener objects.
EventMetaData | The EventMetaData class is intended to wrap any data necessary for event generation. The EventMetaData class is passed to the IPushHandler on initialization.

How the Event Generator Is Implemented

An event generator typically implements the following flow of control:

1. The doInit() method creates and validates connections to the EIS.

2. The setupNewTypes() method processes IEventDefinition objects, creating any structures required for processing.

3. The postEvents() method iteratively invokes one of the two modes of data extraction:

   - Push—The postEvents() method polls the EIS for an event and, if an event exists, postEvent() determines which IEventDefinition objects will receive it. The method then transforms the event data into an IDocument object, using the associated schema, and routes the IDocument object using the IEvent associated with the IEventDefinition object.

   - Pull—The postEvents() method waits for notification of an event. When it receives such notification, it extracts the event data from the PushEvent object and transforms it into an IDocument object in accordance with the schema associated with the event adapter. When all the necessary event data has been put into the IDocument, the IDocument is routed to the correct IEventDefinition objects.

4. The removeDeadTypes() method removes dead IEventDefinition objects from any data structures being used for event processing. Any resources associated with those objects are also freed. IEventDefinition objects are considered dead when the application view to which they belong is undeployed.

5. The doCleanUpOnQuit() method removes any resources allocated during event processing.

Listing 7-2 shows the class declaration for the sample adapter's (pull-mode) event generator.

Listing 7-2 Sample Implementation of the Pull Mode of Data Extraction

>     public class EventGenerator
>       extends AbstractPullEventGenerator

Note: The AbstractPullEventGenerator implements the Runnable interface, which enables it to run on its own thread.

The remaining sections in Step 3a: Create an Event Generator provide more code examples that show how an event generator is implemented with the pull mode of data extraction.

Sample EventGenerator

Listing 7-3 shows a simple constructor for an event generator. You must invoke the parent's constructor so that the parent's members get initialized correctly. The listing then shows how the doInit() method receives configuration information from the map variable and validates the parameters. The sample contains any parameters associated with the event generator at design time.

Listing 7-3 Sample Constructor for an EventGenerator

>     public EventGenerator()
>     {
>     super();
>     }
>         protected void doInit(Map map)
>            throws java.lang.Exception
>         {
>         ILogger logger = getLogger();

>         m_strUserName = (String)map.get("UserName");
>              if (m_strUserName == null || m_strUserName.length() == 0
>         {
>          String strErrorMsg =
>              logger.getI18NMessage("event_generator_no_UserName");
>              logger.error(strErrorMsg);
>          throw new IllegalStateException(strErrorMsg);
>         }
>         m_strPassword = (String)map.get("Password");
>              if (m_strPassword == null || m_strPassword.length() == 0)
>         {
>          String strErrorMsg = logger.getI18NMessage
>             ("event_generator_no_Password");
>          logger.error(strErrorMsg);
>              throw new IllegalStateException(strErrorMsg);
>         }

postEvents() is called from the run method of the parent class, as shown in Listing 7-4. This method polls the EIS to detect the occurrence of a new event. This method is invoked at a fixed interval, which is defined in the web.xml file for the event router.

Listing 7-4 Sample Implementation of postEvents()

>     */  protected void postEvents(IEventRouter router)
>             throws java.lang.Exception
>         {

>         ILogger logger = getLogger();

>         // TODO: a real adapter would need to call into the EIS to
>         // determine ifany new events occured since the last time
>           // this method was invoked. For the sake of example, we'll just
>         // post a single event every time this method gets invoked...
>         // event data will be the current time on the
>         // The system formatted according to the event definition...
>         // we'll look for several event types...

>         Iterator eventTypesIterator = getEventTypes();
>         if (eventTypesIterator.hasNext())

>         {
>          do
>         {

>            // The event router is still interested in this type of event

>            IEventDefinition eventDef = (IEventDefinition)
>               eventTypesIterator.next();
>            logger.debug("Generating event for " + eventDef.getName());

>            // Create a default event (just blank/default data)

>            IEvent event = eventDef.createDefaultEvent();

>            // Get the format for the event

>           java.util.Map eventPropertyMap = eventDef.
>              getPropertySet();
>           String strFormat = (String)eventPropertyMap.get
>              ("Format");
>              if( logger.isDebugEnabled() )
>              logger.debug("Format for event type '"+eventDef.
>                 getName()+"' is '"+strFormat+"'");
>              java.text.SimpleDateFormat sdf =
>                 new java.text.SimpleDateFormat(strFormat);
>             IDocument payload = event.getPayload();
>             payload.setStringInFirst("/SystemTime", sdf.format(new
>                Date()));

>             // let's log an audit message for this...

>             try
>             {
>               logger.audit(toString() + ": postEvents >>> posting event
>                  ["+payload.toXML()+"] to router");
>             }

>               catch (Exception exc)

>             {
>               logger.warn(exc);
>             }

>               // This call actually posts the event to the IEventRouter

>             router.postEvent(event);
>             } while (eventTypesIterator.hasNext());
>             }

>         }// end of postEvents

A real adapter must query the EIS to determine whether any new events have occurred since the last time this method was invoked. A concrete example of such a call, available in the DBMS sample adapter included with the ADK, is the postEvent() method in the EventGenerator.java file:

>     WLI_HOME/adapters/dbms/src/com/bea/adapter/dbms/event/EventGenerator.java

Adding New Event Types

setupNewTypes() is called during refresh to handle any new event types. Typically, an event generator needs to allocate resources in the EIS in order to be able to receive events from the EIS. In the DBMS sample adapter, for example, a trigger is created in the DBMS in order to handle a new event type. The setupNewTypes() method allows you to set up any definitions required to handle a new type. The parent class has already performed (and logged) a sanity-check on the listOfNewTypes() file, so you do not need to perform those tasks.

Listing 7-5 Sample Template for setupNewTypes()

>     protected void setupNewTypes(java.util.List listOfNewTypes)
>        {
>         Iterator iter = listOfNewTypes.iterator();
>            while (iter.hasNext())
>           {
>            IEventDefinition eventType = (IEventDefinition)iter.next();
>           }
>         }

Removing Event Types for Undeployed Application Views

removeDeadTypes() is called during refresh to remove any event types for application views that have been undeployed.

You must execute a cleanup process

To ensure that obsolete event types are no longer handled, you must perform a cleanup process. You should, for example, close resources needed to handle the obsolete event type. Listing 7-6 shows how removeDeadTypes() is implemented.

Listing 7-6 Sample Code Based on removeDeadTypes() Template

>     protected void removeDeadTypes(java.util.List listOfDeadTypes)

>     {
>        Iterator iter = listOfDeadTypes.iterator();
>           while (iter.hasNext())
>     {
>        IEventDefinition eventType = (IEventDefinition)iter.next();

Removing Resources

doCleanUpOnQuit() is called during shutdown of the event generator. This method removes any resources allocated during event processing. The sample adapter stubs in this method. The template for implementing this method is shown in the following listing.

Listing 7-7 Sample doCleanUpOnQuit() Method Call

>     protected void doCleanUpOnQuit()
>         throws java.lang.Exception
>         {
>          ILogger logger = getLogger();
>          logger.debug(this.toString() + ": doCleanUpOnQuit");
>         }
>     }

Step 3b: Implement the Data Transformation Method

Data transformation is the process of taking data from the EIS and transforming it into an XML schema that can be read by the application server. For each event, a schema defines the appearance of the XML output, using the SOM and IDocument class libraries. The following code listings show the sequence of events during the data transformation process:

- Listing 7-8 shows the code that transforms data from the EIS into XML schema.

- Listing 7-9 shows the XML schema created by the code in Listing 7-8.

- Listing 7-10 shows the valid XML document created by the schema shown in Listing 7-9.

Listing 7-8 Sample Code for Transforming EIS Data into XML Schema

>     SOMSchema schema = new SOMSchema();
>     SOMElement root = new SOMElement("SENDINPUT");
>     SOMComplexType mailType = new SOMComplexType();
>     root.setType(mailType);
>     SOMSequence sequence = mailType.addSequence();
>     SOMElement to = new SOMElement("TO");
>     to.setMinOccurs("1");
>     to.setMaxOccurs("unbounded");
>     sequence.add(to);
>     SOMElement from = new SOMElement("FROM");
>     from.setMinOccurs("1");
>     from.setMaxOccurs("1");
>     sequence.add(from);
>     SOMElement cc = new SOMElement("CC");
>     cc.setMinOccurs("1");
>     cc.setMaxOccurs("unbounded");
>     sequence.add(cc);
>     SOMElement bcc = new SOMElement("BCC");
>     bcc.setMinOccurs("1");
>     bcc.setMaxOccurs("unbounded");
>     sequence.add(bcc);
>     SOMElement subject = new SOMElement("SUBJECT");
>     subject.setMinOccurs("1");
>     subject.setMaxOccurs("1");
>     sequence.add(bcc);
>     SOMElement body = new SOMElement("BODY");
>     if (template == null)
>         { body.setMinOccurs("1");
>           body.setMaxOccurs("1");
>         }else
>           { Iterator iter = template.getTags();
>              if (iter.hasNext())
>              { SOMComplexType bodyComplex = new SOMComplexType();
>                 body.setType(bodyComplex);
>               SOMAll all = new SOMAll();
>              while (iter.hasNext())
>              { SOMElement eNew = new SOMElement((String)iter.next());
>                 all.add(eNew);
>              }//endwhile
>               bodyComplex.setGroup(all);
>              }//endif
>          }//endif
>     sequence.add(body);
>     schema.addElement(root);

Listing 7-9 XML Schema Created by Code in Listing 7-8

>     <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
>     <xsd:element name="SENDINPUT">
>         <xsd:complexType>
>             <xsd:sequence>
>                 <xsd:element name="TO" maxOccurs="unbounded"
>                    type="xsd: string"/>
>                 <xsd:element name="FROM" type="xsd:string"/>
>                 <xsd:element name="CC" maxOccurs="unbounded"
>                     type="xsd:string"/>
>                 <xsd:element name="BCC" maxOccurs=
>                    "unbounded" type= "xsd:string"/>
>                 <xsd:element name="BCC" maxOccurs="unbounded"
>                     type="xsd:string"/>
>                 <xsd:element name="BODY" type="xsd:string"/>
>             </xsd:sequence>
>         </xsd:complexType>
>     </xsd:element>

Listing 7-10 Valid XML Document Created by Schema in Listing 7-9

>     </xsd:schema>
>     <?xml version="1.0"?>
>     <!DOCTYPE SENDINPUT>
>     <SENDINPUT>
>       <TO/>
>       <FROM/>
>       <CC/>
>       <BCC/>
>       <BCC/>
>       <BODY/>

>     </SENDINPUT> <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">

Step 4: Test the Adapter

You can test the adapter by using the adapter test harness provided with WebLogic Integration. For a complete description of this tool and instructions for using it, see Step 5: Test the Adapter in Developing a Service Adapter.

Step 5. Deploy the Adapter

After rebuilding the new adapter, deploy it in a WebLogic Integration environment. You can deploy an adapter either manually or from the WebLogic Server Administration Console. For complete information, see Deploying Adapters.