Adapter Development Kit 9.12 | webMethods Adapter Development Kit Documentation | webMethods Adapter Development Kit Installation and User’s Documentation | Usage Scenarios | How to create an adapter listener implementation?
 
How to create an adapter listener implementation?
An adapter connection connects to an adapter resource. This chapter describes how to create an adapter connection implementation.
Pre-requisites:
*webMethods Integration Server 9.12 or later installed.
*Designer 9.12 or later installed.
*Integration Server Administrator access.
*Java 1.8 or later installed.
*Basic understanding of webMethods Integration Server, Designer, Integration Server Administrator, Java.
1. Start the editor to create Java files for adapter listener implementation.
2. Create directories corresponding to your Java package structure in the webMethods package you created using Designer. For example: com\mycompany\adapter\myAdapter\listeners. In the example, the folder created is com\wm\MyAdapter\listeners.
Note:
You must create your Java package and classes in the adapterPackageName\code\source folder in the webMethods package you created using Designer.
3. Create the com.wm.adk.notification.WmConnectedListener implementation class.
In the example, created a SimpleListener class.
a. Create a class attribute, a set method, a constant, and a resource domain for each metadata parameter. For example, a constant FILE_NAME_PARM, a corresponding class attribute _fileName, a corresponding set method setFileName.
Class Attribute Name
Class Attribute Set Method Name
Class Attribute Name Constant
Resource Domain Name Constant
_fileName
setFileName
FILE_NAME_PARM
None
b. In the fillWmDescriptor method, call WmDescriptor.setRequired method, and WmDescriptor.setDescriptions method.
c. In the listenerStartup method, call retrieveConnection method to retrieve connection object and use it to read the file.
d. Implement the waitForData method.
e. In the listenerShutdown method, close the FileReader object.
f. Implement the processNotificationResults method.
For example: the SimpleListener class:
package com.wm.MyAdapter.listeners;

import com.wm.adk.error.AdapterException;
import com.wm.adk.metadata.WmDescriptor;
import com.wm.adk.notification.WmConnectedListener;
import com.wm.adk.notification.NotificationResults;

import java.io.FileReader;
import java.util.Locale;
import javax.resource.ResourceException;

import com.wm.MyAdapter.MyAdapter;
import com.wm.MyAdapter.connections.SimpleConnection;
public class SimpleListener extends WmConnectedListener
{
public static final String FILE_NAME_PARM = "fileName";
private String _fileName = null;
public void setFileName(String val){_fileName = val;}

private FileReader _reader = null;
private StringBuffer workingBuffer = new StringBuffer();
private String _lastDataObject = null;

public void fillWmDescriptor(WmDescriptor descriptor, Locale locale)
throws ResourceException
{
descriptor.setRequired(FILE_NAME_PARM);
descriptor.setDescriptions(
MyAdapter.getInstance().getAdapterResourceBundleManager(), locale);
}
public void listenerStartup() throws ResourceException
{
try
{
//_reader = ((SimpleConnection)retrieveConnection()).getReader();
_reader = ((SimpleConnection)retrieveConnection()).getReader(_fileName);
while(_reader.ready())
{
_reader.read(); // move to the end of the stream
}
}
catch (Throwable t)
{
throw MyAdapter.getInstance().createAdapterException(100,t);
}
}

public Object waitForData() throws ResourceException
{
try
{
if(_reader.ready())
{
do
{
int i = _reader.read();
if (i != -1)
{
char c = (char)i;
workingBuffer.append(c);
if(c == '\n')
{
_lastDataObject = new String(workingBuffer);
workingBuffer = new StringBuffer();
break;
}
}
else
{
break;
}
} while (_reader.ready());
}
}
catch (Throwable t)
{
throw MyAdapter.getInstance().createAdapterException(100,t);
}
return _lastDataObject;
}
public void listenerShutdown()
{
try
{
_reader.close();
}
catch(Throwable t){}
}
public void processNotificationResults(NotificationResults results)
throws ResourceException
{
if(results != null)
{
if(results.hadError())
{
MyAdapter.getLogger().logError(9999,
"Error processing: " + this._lastDataObject +
" errorInfo = " + (results.getErrorInfo() == null ? "" : results.getErrorInfo().toString()));
}
}
else
{
MyAdapter.getLogger().logError( 9999,
"No notification available to process:" +
this._lastDataObject);
}
}
}
4. Add a getReader method in the WmManagedConnection implementation class.
package com.wm.MyAdapter.connections;
..
..
import java.io.FileReader;
import java.io.FileNotFoundException;
..
..
public class SimpleConnection extends WmManagedConnection {
..
..
public FileReader getReader(String fileName) throws AdapterException
{
FileReader _reader = null;

try {
_reader = new FileReader(fileName);
}
catch(Exception e) {
throw MyAdapter.getInstance().createAdapterException(100,e);
}
return _reader;
}
}
5. Update the resource bundle implementation class to add the display name and description of the listener class and the fields in the listener class.
In the example, update MyAdapterResource class's Object[][] _contents as follows:
package com.wm.MyAdapter;
..
..
import com.wm.MyAdapter.listeners.SimpleListener;
..
..
public class MyAdapterResource extends ListResourceBundle implements MyAdapterConstants{
..
..
static final Object[][] _contents = {
..
..
//Listener
,{SimpleListener.class.getName() + ADKGLOBAL.RESOURCEBUNDLEKEY_DISPLAYNAME,
"Simple Listener"}
,{SimpleListener.class.getName() + ADKGLOBAL.RESOURCEBUNDLEKEY_DESCRIPTION,
"Use to monitor log files"}
,{SimpleListener.FILE_NAME_PARM + ADKGLOBAL.RESOURCEBUNDLEKEY_DISPLAYNAME,
"Log File Name"}

}
protected Object[][] getContents() {
// TODO Auto-generated method stub
return _contents;
}
}
6. Register the listener type to the adapter by updating your fillAdapterTypeInfo method in your WmAdapter implementation class.
package com.wm.MyAdapter;
..
..
import com.wm.MyAdapter.listeners.SimpleListener;
..
..
public class MyAdapter extends WmAdapter implements MyAdapterConstants {
..
..
public void fillAdapterTypeInfo(AdapterTypeInfo info, Locale locale)
{
..
..
info.addListenerType(SimpleListener.class.getName());
}
}
7. Execute the ANT script created in adapter definition to compile, and deploy the adapter in Integration Server.
Use the files build.xml and build.properties.
ant deploy
8. Restart Integration Server.
9. Start Integration Server Administrator.
10. In Integration Server Administrator, select Adapters > MyAdapter.
The adapters' connection configured are listed.
11. In the Adapters > MyAdapter > Connections screen, select Configure New Connections.
The adapters' connection types are listed.
12. In the Adapters > MyAdapter > Connection Types screen, select the Connection Type Simple Connection.
The adapters' connection properties to configure are listed:
13. In the Adapters > MyAdapter > Configure Connection Type screen, add the details and Save Connection.
The Adapters > MyAdapter > Connections screen lists the connection you added.
14. In the Adapters > MyAdapter > Connections screen, click No in the Enabled column.
The Enabled column now shows Yes.
15. In Integration Server Administrator, select Adapters > MyAdapter.
The adapters' menu lists Listeners.
16. In the Adapters > MyAdapter > Listeners screen, select Configure new listener.
The adapters' listener types are listed.
17. In the Adapters > MyAdapter > Configure Listener Type screen, select the Listener Type Simple Listener.
The adapters' Simple Listener properties to configure are listed:
18. In the Adapters > My Adapter > Configure Listener Type screen, add the details (File path of the log on which you want to add the listener. For example: C:\softwareag\107\IntegrationServer\instances\default\logs\server.log) and Save Listener.
The Adapters > My Adapter > Listener screen lists the listener you added.
19. In the Adapters > MyAdapter > Listener screen, select Enabled in the Status column.
The Listener column now shows Enabled.
20. Create a class by extending com.wm.adk.notification.WmAsyncListenerNotification base class.
In the example, created a SessionLogListenerNotification class.
a. Create a class attribute, a set method, a constant, and a resource domain for each metadata parameter. For example, a constant FIELD_NAMES_PARM, a corresponding class attribute _fileName, a corresponding set method setFileName.
Class Attribute Name
Class Attribute Set Method Name
Class Attribute Name Constant
Resource Domain Name Constant
_fieldNames
setFieldNames
FIELD_NAMES_PARM
FIELD_NAMES_RD
_fieldTypes
setFieldTypes
FIELD_TYPES_PARM
FIELD_TYPES_RD
_uses
setUses
USES_PARM
None
None
setSignature
SIG_PARM
None
b. In the fillWmTemplateDescriptor method, call WmTemplateDescriptor.createGroup method, WmTemplateDescriptor.createFieldMap method, and WmTemplateDescriptor.createTuple method.
c. In the fillWmTemplateDescriptor method, call WmTemplateDescriptor.setResourceDomain method, and WmTemplateDescriptor.setDescriptions method.
Note:
Use of WmTemplateDescriptor.setResourceDomain for SIG_PARM:
descriptor.setResourceDomain(SIG_PARM,
WmTemplateDescriptor.OUTPUT_FIELD_NAMES,new String[]{
FIELD_NAMES_PARM,FIELD_TYPES_PARM}, USES_PARM);
d. Implement the adapterCheckValue method to return true.
e. Implement the adapterResourceDomainLookup method.
f. In the registerResourceDomain method, call addResourceDomainLookup method for FIELD_NAMES_RD, and FIELD_TYPES_RD.
g. Implement the supports method to parse the data and return true.
h. Implement the runNotification method.
For example, the SessionLogListenerNotification class:
package com.wm.MyAdapter.listeners;

import com.wm.adk.error.AdapterException;
import com.wm.adk.metadata.WmDescriptor;
import com.wm.adk.notification.WmAsyncListenerNotification;
import com.wm.adk.notification.NotificationResults;
import com.wm.adk.notification.AsyncNotificationResults;
import com.wm.adk.notification.NotificationEvent;
import com.wm.adk.connection.WmManagedConnection;
import com.wm.adk.metadata.*;
import com.wm.adk.cci.record.WmRecord;
import com.wm.adk.cci.record.WmRecordFactory;

import javax.resource.ResourceException;
import java.util.Locale;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringTokenizer;

import com.wm.MyAdapter.MyAdapter;
import com.wm.MyAdapter.MyAdapterConstants;
import com.wm.MyAdapter.connections.SimpleConnection;
public class SessionLogListenerNotification extends WmAsyncListenerNotification
implements MyAdapterConstants
{

private String[] _fieldNames = null;
private String[] _fieldTypes = null;
private boolean[] _uses = null;

public void setFieldNames(String[] val){_fieldNames = val;}
public void setFieldTypes(String[] val){_fieldTypes = val;}
public void setUses (boolean[] val){_uses = val;}
public void setSignature(String[] val){}

public static final String NOTIFICATION_SETUP_GROUP =
"SessionLogListenerNotification.setup";

public static final String FIELD_NAMES_PARM = "fieldNames";
public static final String FIELD_TYPES_PARM = "fieldTypes";
public static final String USES_PARM = "uses";
public static final String SIG_PARM = "signature";

public static final String FIELD_NAMES_RD =
"SessionLogListenerNotification.fieldNames.rd";
public static final String FIELD_TYPES_RD =
"SessionLogListenerNotification.fieldTypes.rd";

public static final String[] _sigFieldNames = {
"timeStamp",
"component",
"rootContext",
"parentContext",
"currentContext",
"server",
"eventCode",
"user",
"sessionName",
"RPCs",
"age"};
private Object[] _parsedValues = new Object[_sigFieldNames.length];
public void fillWmTemplateDescriptor(WmTemplateDescriptor descriptor,Locale l)
throws ResourceException
{
String[] parms = new String[] {FIELD_NAMES_PARM,
FIELD_TYPES_PARM,
USES_PARM,
SIG_PARM};
descriptor.createGroup(NOTIFICATION_SETUP_GROUP, parms);
descriptor.createFieldMap(parms, false);
descriptor.createTuple(new String[]{FIELD_NAMES_PARM, FIELD_TYPES_PARM});

descriptor.setResourceDomain(FIELD_NAMES_PARM, FIELD_NAMES_RD, null);
descriptor.setResourceDomain(FIELD_TYPES_PARM, FIELD_TYPES_RD, null);
descriptor.setResourceDomain(SIG_PARM,
WmTemplateDescriptor.OUTPUT_FIELD_NAMES,new String[]{
FIELD_NAMES_PARM,FIELD_TYPES_PARM}, USES_PARM);

descriptor.setDescriptions( MyAdapter.getInstance().
getAdapterResourceBundleManager(), l);
}

public Boolean adapterCheckValue(WmManagedConnection connection,
String resourceDomainName, String[][] values,
String testValue) throws AdapterException
{
return true;
}
public ResourceDomainValues[] adapterResourceDomainLookup(
WmManagedConnection connection, String resourceDomainName,
String[][] values) throws AdapterException
{
ResourceDomainValues[] results = null;
if (resourceDomainName.equals(FIELD_NAMES_RD)
|| resourceDomainName.equals(FIELD_TYPES_RD))
{
ResourceDomainValues names = new ResourceDomainValues(
FIELD_NAMES_RD, _sigFieldNames);
ResourceDomainValues types = new ResourceDomainValues(
FIELD_TYPES_RD,new String[] {
Date.class.getName(), //timestamp
String.class.getName(), // component
String.class.getName(), // rootContext
String.class.getName(), // parentContext
String.class.getName(), // currentContext
String.class.getName(), // server
Integer.class.getName(), // eventCode
String.class.getName(), // user
String.class.getName(), // sessionName
Integer.class.getName(), // RPCs
Long.class.getName() // age
});
results = new ResourceDomainValues[] {names,types};
}
return results;
}
public void registerResourceDomain(WmManagedConnection connection,
WmAdapterAccess access) throws AdapterException
{
access.addResourceDomainLookup(this.getClass().getName(),
FIELD_NAMES_RD, connection);
access.addResourceDomainLookup(this.getClass().getName(),
FIELD_TYPES_RD, connection);
}

public boolean supports(Object data) throws ResourceException
{
boolean result = false;
try
{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd H:mm:ss zzz");
String sData = (String)data;
this._parsedValues[0] = sdf.parse(sData.substring(0,48));
StringTokenizer st = new StringTokenizer(sData.substring(49)," ",false);
this._parsedValues[1] = st.nextToken();
this._parsedValues[2] = st.nextToken();
this._parsedValues[3] = st.nextToken();
this._parsedValues[4] = st.nextToken();
/*
st.nextToken(); // skip the session ID
this._parsedValues[5] = st.nextToken();
this._parsedValues[6] = new Integer(st.nextToken());
this._parsedValues[7] = st.nextToken();
this._parsedValues[8] = st.nextToken();
this._parsedValues[9] = new Integer(st.nextToken());
this._parsedValues[10] = new Long(st.nextToken());
*/
result = true;
}
catch(Throwable t){}
return result;
}

public NotificationResults runNotification(NotificationEvent event)
throws ResourceException
{
NotificationResults result = null;
WmRecord notice = WmRecordFactory.getFactory().createWmRecord("notUsed");
for(int i = 0; i< _sigFieldNames.length;i++)
{
if (_uses[i])
{
notice.put(_sigFieldNames[i],_parsedValues[i]);
}
}
this.doNotify(notice);
result = new AsyncNotificationResults(this.nodeName(),true,null);
return result;

}
}
21. Update the WmManagedConnection implementation class, SimpleConnection.
a. Update the WmManagedConnection.registerResourceDomain method to register the listener notification metadata parameters for lookup.
In the example, update SimpleConnection.registerResourceDomain method as follows:
package com.wm.MyAdapter.connections;
..
..
import com.wm.MyAdapter.listeners.SessionLogListenerNotification;
..
..
public class SimpleConnection extends WmManagedConnection {
..
..

public void registerResourceDomain(WmAdapterAccess access)
throws AdapterException
{
..
..

//SessionLog Listener Notification Registering Resource Domain
access.addResourceDomainLookup(SessionLogListenerNotification.FIELD_NAMES_RD, this);
access.addResourceDomainLookup(SessionLogListenerNotification.FIELD_TYPES_RD, this);
}
}
b. Update the adapterResourceDomainLookup method to add the resource domain lookup of the listener notification metadata parameters.
In the example, update SimpleConnection.adapterResourceDomainLookup method as follows:
package com.wm.MyAdapter.connections;
..
..
import java.util.Date;
..
import com.wm.MyAdapter.listeners.SessionLogListenerNotification;
..
..
public class SimpleConnection extends WmManagedConnection {
..
..

public ResourceDomainValues[] adapterResourceDomainLookup(String serviceName,
String resourceDomainName, String[][] values) throws AdapterException
{
..
..

//Listener notification
else if (resourceDomainName.equals(SessionLogListenerNotification.FIELD_NAMES_RD)
|| resourceDomainName.equals(SessionLogListenerNotification.FIELD_TYPES_RD))
{
ResourceDomainValues names = new ResourceDomainValues(
SessionLogListenerNotification.FIELD_NAMES_RD, SessionLogListenerNotification._sigFieldNames);
ResourceDomainValues types = new ResourceDomainValues(
SessionLogListenerNotification.FIELD_TYPES_RD,new String[] {
Date.class.getName(), //timestamp
String.class.getName(), // component
String.class.getName(), // rootContext
String.class.getName(), // parentContext
String.class.getName(), // currentContext
String.class.getName(), // server
Integer.class.getName(), // eventCode
String.class.getName(), // user
String.class.getName(), // sessionName
Integer.class.getName(), // RPCs
Long.class.getName() // age
});
results = new ResourceDomainValues[] {names,types};
}
}
22. Update the resource bundle implementation class to add the display name and description of the listener notification class and the fields in the listener notification class.
In the example, update MyAdapterResource class's Object[][] _contents as follows:
package com.wm.MyAdapter;
..
..
import com.wm.MyAdapter.listeners.SessionLogListenerNotification;
..
..
public class MyAdapterResource extends ListResourceBundle implements MyAdapterConstants{
..
..
static final Object[][] _contents = {
..
..
//SessionLog Listener Notification
,{SessionLogListenerNotification.class.getName() + ADKGLOBAL.RESOURCEBUNDLEKEY_DISPLAYNAME,
"SessionLog Listener Notification"}
,{SessionLogListenerNotification.class.getName() + ADKGLOBAL.RESOURCEBUNDLEKEY_DESCRIPTION,
"Use SessionLog Listener Notification to monitor log files"}
,{SessionLogListenerNotification.FIELD_NAMES_PARM + ADKGLOBAL.RESOURCEBUNDLEKEY_DISPLAYNAME,
"Field Name"}
,{SessionLogListenerNotification.FIELD_NAMES_PARM + ADKGLOBAL.RESOURCEBUNDLEKEY_DESCRIPTION,
"Field Name To Check"}
,{SessionLogListenerNotification.FIELD_TYPES_PARM + ADKGLOBAL.RESOURCEBUNDLEKEY_DISPLAYNAME,
"Field Types"}
,{SessionLogListenerNotification.FIELD_TYPES_PARM + ADKGLOBAL.RESOURCEBUNDLEKEY_DESCRIPTION,
"Field Types To Check"}

}
protected Object[][] getContents() {
// TODO Auto-generated method stub
return _contents;
}
}
23. Register the listener notification type in the adapter by updating your fillAdapterTypeInfo method in your WmAdapter implementation class.
package com.wm.MyAdapter;
..
..
import com.wm.MyAdapter.listeners.SessionLogListenerNotification;
..
..
public class MyAdapter extends WmAdapter implements MyAdapterConstants {
..
..
public void fillAdapterTypeInfo(AdapterTypeInfo info, Locale locale)
{
..
..
info.addNotificationType(SessionLogListenerNotification.class.getName());
}
}
24. In Designer, create the Adapter Notification.
a. In Package Navigator, select the Default package.
b. Select File > New > Folder.
c. Enter the Folder name. For example: TestMyAdapterListener.
d. In the Package Navigator, select the Default > TestMyAdapter.
e. Select File > New > Adapter Notification.
f. Enter the Element name and click Next. For example: TestMyAdapterListenerNotification.
g. In the Select Adapter Type screen, select an adapter type for which you want to create the service. For example: MyAdapter.
h. In the Select a Template screen, select a listener notification template and click Finish. For example: SessionLog Listener Notification.
i. In the Select an Adapter Notification Listener screen, select an adapter listener. For example: TestMyAdapter:List1.
j. In the Publish Document Name screen, select Finish.
In Designer, you can see the following two items created:
a. A new adapter notification TestMyAdapterListenerNotification is created with two tabs: SessionLogListenerNotification.setup, and Adapter Settings.
b. A new Document Type TestMyAdapterListenerNotificationPublishDocument is created with two tabs: Tree, and Comments.
25. Create a Flow Service for the Listener Notification Node using Designer.
a. Navigate to the folder Default > TestMyAdapter.
b. Create the Flow Service.
c. In the Create a New Flow Service screen, add TestMyAdapterFlowService in the Element name field and click Finish.
d. In the Flow Service > Tree tab, right-click and select Insert > savePipelineToFile.
e. In the Flow Service > Tree tab, select the savePipelineToFile method. You can see the Service In > fileName in the Pipeline tab.
f. Update Service In > fileName in the Pipeline tab. In this example, the value is MonitorListenerNotificationPipeline.log.
g. Click OK, and save the flow service.
26. Create a trigger using Designer.
a. Navigate to the folder Default > TestMyAdapter.
b. Create the webMethods Messaging Trigger and select Finish. In the example, TestMyAdapterListenerMsgTrigger is created.
c. In the webMethods Messaging Trigger, Trigger Settings tab, Condition Detail section, perform the following:
*In the trigger editor, in the Conditions section, accept the default Condition1.
*In the Condition detail section, in the Service field, select or type the flow service name TestMyAdapterFlowService.
*Click to insert the Document Type TestMyAdapterListenerNotificationPublishDocument.
d. Save the messaging trigger.
27. Enable the listener notification.
a. Start Integration Server Administrator
b. In Adapters > MyAdapter > Listener Notifications screen, click No in the Enabled column for the listener notification.
The file MonitorListenerNotificationPipeline.log is created in Integration Server_directory /instances/<instance_name>/pipeline/ folder. This file contains one entry each time the file added in the listener is updated:
<?xml version="1.0" encoding="UTF-8"?>

<IDataXMLCoder version="1.0">
<record javaclass="com.wm.data.ISMemDataImpl">
<value name="fileName">MonitorListenerNotificationPipeline.log</value>
<record name="TestMyAdapterListener:TestMyAdapterListenerNotificationPublishDocument"
javaclass="com.wm.data.ISMemDataImpl">
<Date name="timeStamp" type="java.util.Date">Thu Oct 21 12:34:45 IST 2021</Date>
<value name="component">)</value>
<value name="rootContext">Unable</value>
<record name="_env" javaclass="com.wm.data.ISMemDataImpl">
<value name="locale"></value>
<value name="activation">wm624455fb0-b66d-4344-b92f-cee92639b14c</value>
<value name="businessContext">wm6:2281c61b-a0dd-4dc2-bb31-6eaea2052a1c\snull\snull:
wm624455fb0-b66d-4344-b92f-cee92639b14c:null:IS_61:null</value>
<value name="uuid">wm:14c48f70-323e-11ec-b7fd-000000000152</value>
<value name="trackId">wm:14c48f70-323e-11ec-b7fd-000000000152</value>
<value name="pubId">islocalpubid</value>
<Date name="enqueueTime" type="java.util.Date">Thu Oct 21 12:41:15 IST 2021</Date>
<Date name="recvTime" type="java.util.Date">Thu Oct 21 12:41:15 IST 2021</Date>
<number name="age" type="java.lang.Integer">0</number>
</record>
</record>
</record>
</IDataXMLCoder>