Apama 10.15.0 | Connecting Apama Applications to External Components | Correlator-Integrated Support for the Java Message Service (JMS) | Using the Java Message Service (JMS) | Mapping Apama events and JMS messages | Implementing a custom Java mapper
 
Implementing a custom Java mapper
If the mapping tools provided with Apama do not meet your needs, then you can implement your own Java class to map between Apama event strings and JMS message objects. A custom mapper can handle some event types and delegate handling of other event types to the mapping tools provided with Apama or to other custom mapping tools. Typically, you will want to use the SimpleAbstractJmsMessageMapper class, which is in the com.apama.correlator.jms.config.api.mapper package. This topic provide a general description of how to implement a custom mapper. See the Javadoc for details.
API overview
The SimpleAbstractJmsMessageMapper class is a helper class for implementing simple mappings between JMS messages and Apama events. This class is the recommended way to implement a stateless, bidirectional mapper, with trivial implementations of methods that most implementors will not need to be concerned with. Most implementations will need to override only the following methods:
*The mapApamaToJmsMessage() method converts an Apama event string and (possibly null) unique identifiers for elimination of duplicate messages to a JmsSenderMessageHolder object that contains the message and message-sending parameters.
abstract JmsSenderMessageHolder mapApamaToJmsMessage(
JmsSenderMapperContext context, MappableApamaEvent event)
*The mapJmsToApamaMessage() method converts a JMS message object to an Apama event string. It can also add the Apama unique message identifier (if it is available) into the Message object for elimination of duplicate messages.
abstract MappableApamaEvent mapJmsToApamaMessage(
JmsReceiverMapperContext context, javax.jms.Message message)
Typically, the mapper class would contain com.apama.event.parser.EventType fields for each of the Apama event types the mapper can handle. Your custom mapper methods will use these fields to parse and generate Apama events. Both methods use MappableApamaEvent, which wraps an Apama event string plus optional duplicate detection information.
A JmsSenderMessageHolder object wraps a JMS message object in addition to JMS send parameters such as the JMS destination.
The JmsReceiverMapperContext and JmsSenderMapperContext objects give mappers access to helper methods for
*Converting between strings and JMS destinations
*Performing JNDI lookups if JNDI is configured
*Obtaining other contextual information that may be needed during mapping such as the receiverId or senderId
The SimpleAbstractJmsMessageMapper class also provides optional senderMapperDelegate/receiverMapperDelegate bean properties that identify another mapper to use for any messages that this mapper does not handle. The associated methods are used for the XML configuration but should not be called by subclasses.
If your custom mapper requires configuration properties to be specified in the XML configuration file then define the properties as standard Java bean get/set public methods. Include any logic required to validate parameter values in the overridden JmsSenderMessageHolder.init() method.
For more complex needs, do not use the SimpleAbstractJmsMessageMapper class. Instead, implement the factory interfaces directly to create separate classes for the sender and receiver mappers. This is particularly important when the mapping operations are stateful. For example, if they rely on a cache that should not be shared across all the mapper instances created by the factory to avoid thread-safety concerns or costly and unnecessary synchronization. For the receiver side (sender side is identical) there are two interfaces:
*JmsReceiverMapperFactory is the interface that must be implemented by the Java bean, holding any required configuration information. This class will be referenced in the XML configuration file. It provides get/set methods for configuration properties, an init() method to perform any validation and a factory method to create JmsReceiverMapper instances.
*JmsReceiverMapper is the interface that is responsible for actually mapping the objects. A new instance will be created for each receiver and for each thread on which mapping occurs, so this instance can hold any required caches or state without the need for costly locking/synchronization. A destroy() method is provided in case there are resources that need to be cleared or closed when the associated receiver is shut down is removed.
Configuring a custom mapper
To configure a JMS connection to use a custom mapper class, edit the connection's XML configuration file as follows:
*Add a bean definition for the sender and/or receiver mapper factory class, with any associated configuration, and an id attribute that will be used to identify this mapper bean in the rest of the configuration. Usually the simplest way to specify the classpath for the custom mapper's classes is to put the mapper bean definition inside a new <jms:classpath> element.
*Under the jms:connection element, set the receiverMapper and/or senderMapper properties to point to this mapper, typically you use a ref="beanid" attribute to do this. If these properties are not specified explicitly, the default is that the connection uses the Apama-provided mapper, assuming it is the only mapper defined in the configuration.
Following is an example of a configuration that specifies custom mappers:
<jms:classpath classpath="mycp">
<bean id="myCustomMapper" class="MyMapper">
<!-- if this uses SimpleAbstractJmsMessageMapper, optionally specify the
factory bean to delegate to for messages this mapper does not handle. -->
<property name="senderMapperDelegate" ref="standardMapper"/>
<property name="receiverMapperDelegate" ref="standardMapper"/>

<!-- mapper-specific configuration could go here -->
</bean>
</jms:classpath>

<jms:classpath classpath="...">
<jms:connection id="myConnection">

<property name="senderMapper" ref="myCustomMapper"/>
<property name="receiverMapper" ref="myCustomMapper"/>
Any mapper that subclasses SimpleAbstractJmsMessageMapper also supports the optional properties senderMapperDelegate and receiverMapperDelegate These properties can be used to specify a fallback mapper (factory bean) to delegate to for message types this mapper does not support. Map methods must return null to indicate such types. For other errors, exceptions should always be thrown.