Mapping Apama events and JMS messages

After you specify which Apama events you want to associate with JMS messages, you need to create mapping rules that associate Apama event fields with parts of the JMS messages. Apama’s adapter editor in Apama Plugin for Eclipse provides a visual mapping tool to create the mapping rules. There are several approaches for how to map Apama events to the JMS messages - these are explained in the topics below.

In addition, you can also specify transformation types:

  • XSLT transformation type. Use this approach when receiving JMS messages containing XML to change or simplify the structure of the XML document.
  • XPath XML transformation type. Use this approach when receiving JMS messages containing XML to specify values from the XML document that are to be used to populate the fields in the target Apama event.
  • XMLDecode transformation type. Use this approach when receiving JMS messages containing XML and multiple rules are working off of the same XML source.

For more information, see Specifying transformation types.

Simple mapping for JMS messages

Use this approach when a simple Apama event field can be associated with a corresponding value in the JMS message.

When creating a simple 1:1 mapping rule for an Apama event field to part of a JMS message that contains a similar type, you can drag a line between the elements as described below.

To drag a line

  1. In the editor for the correlator-integrated adapter for JMS, display the Event Mapping tab.

  2. For each mapping rule, click on the entity you want to map and drag a line to the entity you want to map it to.

Each rule is represented with a blue line between entities. If the types of the source and target do not match, type coercion will be performed automatically at runtime.

Using expressions in mapping rules

Use this approach when sending or receiving JMS messages and you need to write a customized JUEL expression for a mapping rule.

In many cases, a mapping rule requires customization. For example, if you map an event field to a JMS Property field, then you need to specify which JMS property to use. In other cases, you may want to use a constant value in a mapping rule or to create a JUEL expression, for example to execute an XPath query on nested XML documents.

To add an expression to a mapping rule

  1. Drag a mapping line from the entry in the source tree to the target. If one side of the mapping rule requires a more specific expression, the Connection Participants dialog is displayed.

  2. In the Connection Participants dialog’s Type field, select an entry from the drop-down list.

  3. In the next field enter the JMS Body type, the JMS Property name, a constant value, or a custom JUEL expression. As you enter this information, the expression that will be used in the mapping rule is displayed in the Expression Value field.

  4. Click OK.

For a table of expressions for getting and setting values in JMS messages and recommended mappings to Apama event types, see JUEL mapping expressions reference for JMS.

Template-based XML generation

Use this approach when sending JMS messages that contain XML. You assign a template that will be used to generate an XML document. The template contains placeholders for each of the source event fields whose values will replace the placeholders.

With the template-based approach to mapping, you can map fields in an Apama event to elements and attributes in complex XML structures. The template consists of a sample XML document with placeholders that will be replaced with values from the Apama event fields. When you assign a template, these variables are displayed in the JMS message tree. You then map event fields to the variables.

To assign a template for mapping

  1. In the adapter editor’s Event Mappings tab, right-click the JMS Body entry and select Assign Template. The Assign Template dialog appears.

  2. In the XML Template file field, enter the name of the template file you want to use or click the browse or down arrow button to locate the file.

    When you specify a template file, the contents of the file are added to the text field in the dialog.

    It is usually best to create the template file from a sample XML document before opening this dialog, but it is also possible to perform this task from the dialog itself, for small XML documents. To create the XML template, you define placeholders to represent field values that you want the adapter to obtain from the input event. To define a placeholder, insert a dollar sign ($) following by the placeholder name. After you click OK, the placeholder appears as a new child of the target’s JMS body node.

  3. In the source event, click the Apama event field and drag a line to the desired element or attribute in the target JMS message.

Adding multiple XPath mapping rules for a received XML document

Use this to configure a set of XPath mappings, based on an XML schema or sample XML document using the Treat as dialog.

Using Treat As on JMS Body dialog - Sender

In the Mapping Definitions section of the Sender Mapping Configuration, when you right-click JMS Body: Text Message and select the Treat as… option, the Treat As dialog appears. The Treat As dialog allows you to select the type of JMS message to be sent.

To select a base type for the JMS body

  1. Select the base type of the JMS body type in the Body Type field. If you are mapping to a bytes message, the UTF-8 encoding is used to convert the character string to a bytes message.

  2. Click OK.

Using Treat As on JMS Body dialog - Receiver

In the Mapping Definitions section of the Receiver Mapping Configuration, when you right-click JMS Body: Text Message and select the Treat as… option, the Treat As dialog appears. The Treat As dialog allows you to select a base type for the JMS body to treat a node in the mapper.

To select a base type for the JMS body

  1. Select the base type of the JMS body type in the Body Type field. If you select Text Message or Bytes Message, you must also select one of the following options:

    • Select the String option if you have selected Text Message in the Body Type field.
    • Select the Bytes option if you have selected Bytes Message in the Body Type field. If the JMS body is a bytes message, the UTF-8 encoding is used to convert it into a character string.
    • Select the XML option and browse for the required XML file.
    • Select the XML using schema option and browse for the required schema file using the Type Chooser dialog. See Using the Type Chooser dialog for more information on using the Type Chooser dialog. The Element name is automatically populated if it is defined for the chosen schema file.
  2. Click OK.

Using the Type Chooser dialog

The Type Chooser dialog allows you to choose and display types from files in different source locations. It supports:

  • The file types XML, XSD, WSDL, and Schema
  • Selecting a source file from a local and remote URL
  • Retrieving and displaying all types from the selected file
  • Global searches of specific types
  • WSDL service files and multilevel imports

To choose a type

  1. Click the drop-down arrow beside the Source field to select a location where the system can find the type definitions. The following options are available:

    Option Description
    Recent Displays the recently selected file locations.
    Local File System Selects a file in your local file system. Selecting this option displays the Open dialog; it also determines which dialog is opened when you subsequently select (Open).
    Workspace Selects a file in your workspace. Selecting this option displays the Select Resource dialog in which you can filter and select a Schema, XML, XSD, or WSDL file extension. You can also select a custom file extension (if any).
    Remote URL Selects a file from a web-based URL. Selecting this option displays the Select Remote URL dialog in which you can specify the remote URL for the file.
    XML Schema Selects a built-in type defined by the XML schema specification. Selecting this option displays the appropriate choices in the bottom pane; it also determines the system behavior when you subsequently select (populates the XML schema types again).

    You can search for these types of files: XML, XSD, and WSDL. In addition, you can use custom file types (if any) and built-in XML Schema types.

  2. Optionally, click to search for a file that you recently selected.

    The bottom pane of the dialog displays the types available in the selected file.

  3. To filter the types and display a specific type, enter the first few characters of the type that you want to select in the type filter text field. The supported pattern characters are: ? for single character and * for a string.

Using the XPath Helper

The XPath Helper enables you to generate and evaluate XPath expressions. It implements all features defined in the W3C Recommendation for the XML Path Language (XPath). See also http://www.w3.org/TR/xpath/ (Version 1.0) and http://www.w3.org/TR/xpath20/ (Version 2.0).

Launching the XPath Helper

When you open an XML file in the XML editor, an XML menu is shown in the Apama Plugin for Eclipse menu bar.

Note:

In some cases, there might be different ways of launching the XPath Helper. Depending on the context you launch it in you can use the Input document field to select a different XML document.

To launch the XPath Helper

  • Do one of the following:

    • Open an XML document in the XML Editor, and from the XML menu, choose XPath Helper.
    • Or select the XML document in the Project Explorer view, invoke the context menu and then choose XML > XPath Helper. The XPath Helper dialog is shown in both cases. The content of the current XML document is shown in the left pane in the form of a tree, consisting of XML elements and their values.

    The path to the current XML document is shown in the Input document field. If you want, you can also select a different XML document using this field.

    Note:

    In some cases, you might not be able to use the Input document field, as its accessibility depends on the context in which you launch the XPath Helper.

Setting node properties

The XPath Helper enables you to set a node in the document tree (shown in the left pane of the XPath Helper dialog) as the target, key, or root node.

To set node properties

  1. Right-click a node in the left pane of the XPath Helper dialog.

  2. From the resulting context menu, choose one of the following commands:

Command

Description

Set Target

Sets the node as the target (indicated with a red circle). Enables you to test whether the selected node is present in the result set of an XPath expression. It also enables you to test whether that node appears once or multiple times in the result set. When you evaluate the XPath expression, the number of hits found for the selected node is shown in the Results pane. If the target node appears in the result set only once, the Results pane displays the message “Reached target uniquely”, and the target indicator (red circle) in the left pane of the XPath Helper dialog changes to a check mark. If the node appears more than once, it displays the message “Reached target” with the target indicator changed to a check mark. If it does not appear at all, it displays the message “Did not reach target” and the target indicator remains as it is.

Remove Target

Removes the current target.

Set Key

Sets the selected node as the key (indicated by a yellow key symbol) and displays the appropriate XPath expression in the text area of the XPath Expression tab. Each time you set a key, a text box is shown next to the key symbol where you can enter a condition or constraint.

You can also enter a condition or constraint in the text area of the XPath Expression tab.

Remove Key

Removes the current key.

Set Root

Sets the selected node as the root. Only the subtree starting from this node will be considered as the input parameter for evaluating and generating XPath expressions.

Remove Root

Removes the current root.

Generating XPath expressions

The XPath Helper enables you to generate XPath expressions. XPath expressions are used to select nodes or node sets in an XML document.

To generate an XPath expression

  1. In the document tree in the left pane of the XPath Helper dialog, double-click a node to display the expression on the XPath Expression tab.

  2. Select one of the following XPath generation options from the corresponding button at the top of the XPath Expression tab (initially Prefix is shown as the button name, indicating that this option is currently selected):

    Option Description
    Prefix Includes prefixes in the generated XPath expression. The XPath Helper resolves these prefixes to their corresponding namespaces, depending upon the namespace entries listed on the Namespaces tab.
    Namespace Includes the required namespaces in the generated XPath expression. This generated XPath expression is independent of the list of namespaces on the Namespaces tab.
    Local Name Generates an XPath expression that is independent of the prefixes and namespaces used in the input document.
  3. In addition, you can select the following options:

    Icon Option Description
    Icon Recent XPath Expressions Displays 10 recent XPath expressions. You can select a different expression for evaluation.
    Icon Use Index Inserts an index in the XPath expression to identify the position of the element.
    Icon Invoke Text Function Generates an XPath expression that invokes the text() function in the last location step.
    Icon Select Node Depth Limits the number of location steps in an XPath expression and determines whether the expression is relative or absolute.

With each selection, the appropriate XPath expression is shown in the text area of the XPath Expression tab, ready to be evaluated.

Note: You can also modify the default generated XPath expression in the text area.

Adding and removing namespaces

The XPath Helper dialog displays a list of namespaces on the Namespaces tab. They are used for evaluating the XPath expression. You can add or remove namespace entries from this tab.

To add or remove a namespace

  1. Select the Namespaces tab in the right pane of the XPath Helper dialog.

    A list of all namespaces found in the root node of the document tree is shown.

  2. To add a namespace:

    1. Click Plus icon.

    2. In the resulting Add Namespace dialog, specify a prefix and a namespace URI.

    3. Click OK.

  3. To remove a namespace:

    1. On the Namespaces tab, select the namespace you want to remove.

    2. Click X icon.

    3. Click OK to confirm the removal of the namespace.

Evaluating XPath expressions

After you have generated an XPath expression, you can evaluate it.

To evaluate an XPath expression

  1. Either use the expression that is currently shown on the XPath Expression tab, or click Icon and select another expression.

  2. Select either Version 1.0 or Version 2.0 of the W3C Recommendation for the XML Path Language (XPath). To do so, use the drop-down list of the corresponding button at the top of the XPath Expression tab (initially Version 2.0 is shown as the button name, indicating that this option is currently selected).

  3. Do one of the following:

    • Click Evaluate to evaluate the generated expression.
    • Or select Auto Evaluate to enable XPath Helper to continuously display results as you double-click a node in the document tree or manually enter the XPath expressions. The XPath Helper evaluates the expression according to the selected criteria and shows the results in the Results pane, in a tree format. You can expand and collapse the tree if it has subnodes.

Using convention-based XML mapping with JMS messages

Use this approach to parse or generate XML documents by using event definitions that follow specific conventions to implicitly encode the structure of the XML document. This approach allows mapping of sequences to elements of the same type. It avoids the need for XPath, but does impose some limitations on the XML naming and structure.

The topics below explain how to use convention-based XML mapping with JMS messages.

Convention-based mapping allows XML documents to be created or parsed based on a document structure encoded in the definition of the source or target Apama event type.

The first stage when using convention-based mapping is to examine the structure of the XML document, and create an event definition to represent its root element, with fields for each attribute, text node, sub-element or sequence (of attributes, text nodes or sub-elements). The actual names of the event types are not important, but the event field names and types must follow the following conventions:

  • XML attributes can be represented by any EPL simple type such as string or integer. The name used should be preceded by an underscore, for example boolean _flag;.
  • XML text nodes are represented by either:
    • A field inside an Apama event representing the parent of the element containing the text, named after the element that encloses the text such as string myelement;. This avoids the need to create an event type to represent the element in cases where the element only contains a text node, and no attributes or children. The field type may be any primitive EPL type (for example, string or integer).
    • A field inside an Apama event representing the element that directly contains the text, named xmlTextNode. This is necessary in cases where an Apama event type is needed to represent the element so that attributes and/or child elements can also be mapped. The field type may be any primitive EPL type (for example, string or integer).
  • XML elements containing attributes or sub-elements of interest are represented by a field of an event type which follows these same conventions. The event type can have any name, but the field must be named after the element, for example, MyElementEventType myelement.
  • XML attributes, text nodes or elements which may occur more than once in the document are represented by a sequence field of the appropriate primitive or event type, named after the element, for example, sequence<string> myelement or sequence<MyElement> myelement.
  • A field of the optional type is processed in the same way as the contained type. If the optional value is empty, then it is not processed when creating XML. Similarly when creating an Apama event from XML, if a node corresponding to an optional field is absent, then the field will have an empty value.

Some special cases to be aware of when naming fields to match element/attribute names are:

  • XML nodes which are inside an XML namespace are always referenced by their local name only (the namespace or namespace prefix is ignored).
  • When generating an XML document, each field in the event will be processed in order and used to build up the output document.
  • When parsing an XML document, each field in the event will be populated with whatever XML content matches the field name and type (based on the conventions above); any XML content that is not referenced in the event definition will be silently ignored.
  • XML node names that are Apama EPL keywords (such as <return>) must be escaped in the event definition using a hash character, for example, string #return;.
  • XML node names containing any character that is not a valid EPL identifier character (anything other than than a-z, A-Z, 0-9 and _) must be represented using a $hexcode escape sequence. Of the characters that are not valid EPL identifier characters, only the hyphen and dot are supported. Note that the hexcode based escape sequences are case sensitive. For representing the hyphen or dot use the following:
    • Hyphen (-) is represented as $002d.
    • Dot (.) is represented as $002e.

You can generate event type definitions automatically from an XML schema using Apama Plugin for Eclipse. See Creating new event definition files for EPL applications.

Limitations of convention-based XML mapping

In this release it is not possible to generate documents that contain elements in different XML namespaces (although when parsing this is not a problem).

The following limitations apply to the Apama event definitions that can be used to generate XML:

  • Dictionary event field types are not supported.
  • If an event field is of type sequence, the sequence can contain simple types or events. The sequence cannot contain sequences of sequences or sequences of dictionaries.
  • Sequences of optional types are not supported.

Convention-based JMS message mapping example

The following example shows how to parse a JMS message whose body contains an XML document and map it to an Apama event called MyEvent.

Consider a JMS message whose body contains the following XML document:

<?xml version='1.0' encoding='UTF-8'?>
<myroot xmlns:p='http://www.myco.com/dummy-namespace'>
  <myelement1>An element value</myelement1>
  <myelement2 myattribute='123' myboolattribute='true'>456</myelement2>
    <ignoredElement>XML content that is not included in the event definition
             is ignored</ignoredElement>
    <e1>Hello</e1>
    <e1>there</e1>
    <e-2 e2att='value1'><subElement>e2-sub-value1</subElement></e-2>
	<e-2 e2att='value2'><subElement>e2-sub-value2</subElement></e-2>
	<e1>world</e1>
	<namespacedElement xmlns='urn:xmlns:foobar'>My namespaced
	      text</namespacedElement>
	<p:namespacedElement>My namespaced text 2</p:namespacedElement>
	<namespacedElement>My non-namespaced text 3</namespacedElement>
	<return>Element whose name is an EPL keyword</return>
</myroot>

Define the Apama event MyEvent as follows:

event MyElement2
{
   string _myattribute;
   boolean _myboolattribute;
   string xmlTextNode;

}

event E2
{
   string _e2att;
   string subElement;
}

event MyRoot
{
   string myelement1;
   MyElement2 myelement2;
   sequence<string> e1;
   sequence<string> namespacedElement;
   string #return;
   sequence<E2> e$002d2;
}

event MyEvent
{
   string destination;
   MyRoot myroot;
}

Note that the field names and types matter but the event type names do not.

The document above would be parsed to the following Apama event string:

MyEvent("queue:MyQueue",
	MyRoot("An element value",
		MyElement2("123",true,"456"),
		["Hello","there","world"],
		["My namespaced text","My namespaced text 2","My non-namespaced text 3"],
		"Element whose name is an EPL keyword",
		[E2("value1","e2-sub-value1"),E2("value2","e2-sub-value2")]
))

The exact same event definitions could be used in the other direction for creating an XML document, although the node order will be slightly different from that of the document shown above (based on the field order) and everything would be in the same XML namespace.

For the above example, the following XML is generated with http://www.example.com/myevent as the namespace and p as the namespace prefix:

<p:myroot xmlns:p="http://www.example.com/myevent">
     <p:myelement1>An element value</p:myelement1>
     <p:myelement2 myattribute="123" myboolattribute="true">456</p:myelement2>
     <p:e1>Hello</p:e1>
     <p:e1>there</p:e1>
     <p:e1>world</p:e1>
     <p:namespacedElement>My namespaced text</p:namespacedElement>
     <p:namespacedElement>My namespaced text 2</p:namespacedElement>
     <p:namespacedElement>My non-namespaced text 3</p:namespacedElement>
     <p:return>Element whose name is an EPL keyword</p:return>
     <p:e-2 e2att="value1">
                 <p:subElement>e2-sub-value1</p:subElement>
     </p:e-2>
     <p:e-2 e2att="value2">
                 <p:subElement>e2-sub-value2</p:subElement>
     </p:e-2>
</p:myroot>

Using convention-based XML mapping when receiving/parsing messages

To map a received JMS message to an Apama event using the convention-based approach

  1. Create an Apama event type with fields that correspond in type and order to the structure of the XML document. Ensure that the target event type you are mapping to has a field of this type and that the field’s name is the same as the name of the root element in the expected XML document.

  2. Drag a mapping line from the JMS message node containing the XML document (for example, the JMS Body) to the target Apama event field that has the same name as the root element in the XML document. Assuming the JMS message contains an XML document (a string beginning with an open angle bracket character “<” ), the document will be parsed and the results will be used to populate the fields of the target event.

Using convention-based XML mapping when sending/generating messages

To map an Apama event to a JMS message using the convention-based approach

  1. Create an Apama event type with fields that correspond in type and order to the structure of the XML document. To use convention-based XML mapping, the event type representing the XML document must be nested inside a parent event type, so ensure that such a parent event type has been created. Typically, the parent event type might have two fields, a string field representing the JMS destination, and an event field representing the root of the XML document.

  2. In the adapter editor’s Event Mappings tab, click the Add Event button (Plus icon) to add a mapping for the desired parent event type (that is, the event type that contains the event field that represents the XML root element).

  3. In the adapter editor’s Event Mappings tab, right-click the Apama event that represents the root node of the XML document and select Add Computed Node to display the Add Computed Node dialog.

  4. In the Add Computed Node dialog’s Select Method field, select Convert to XML from the drop-down list. The dialog is updated to show more information.

    You can specify a namespace and namespace prefix for the generated XML document if desired, or else leave them blank. By default, the Include empty fields option is enabled. This specifies that empty XML nodes will be generated when empty EPL string fields are encountered within an Apama event. This option does not affect empty strings within a sequence of EPL strings. If you clear the check box to disable the option, empty XML nodes will not be generated.

  5. Click OK.

    In the mapping tree, an entry of type Convert To XML is added to the selected event node.

  6. Drag a mapping line from the Convert To XML entry to the desired node in the XML message, for example, to JMS Body.

Combining convention-based XML mapping with template-based XML generation

It is also possible to combine the convention-based approach with template-based XML generation. An XML template can be used to generate the top-level XML document, while one or more placeholders can be added and mapped to XML sub-document strings generated by convention-based XML mapping.

To combine these approaches

  1. Right-click a source event (representing an XML root element) or sequence (representing a list of XML elements) and click Add Computed Node.
  2. Select Convert to XML from the drop-down list and click OK. This will result in a Convert To XML node representing a generated XML string.
  3. Drag a mapping line from that node to the target $placeholder node specifying where the XML snippet should be inserted into the top-level document.

Add Computed Node dialog

The Add Computed Node dialog provides an efficient means of adding customized entries to the mapping rules in the Mapping Definitions section. This feature helps you take advantage of the following benefits:

  • You do not need to re-enter the custom entries each time you want to map a node.
  • You can modify the entry dynamically for all mappings rules generated from a node at once, by simply modifying the entry.

To add customized entries to the mapping rules

  1. By default, the Type is selected as Computed Node. Depending on the customization you want to add before the mapping, select one of the following from the drop-down list in the Select method field:

    • String.concat to concatenate the specified string to the end of this string. The Display name field appears with a default value. Edit the value if you want. Enter the String in the String field that you want to concatenate in the Method Arguments section.

    • String.contains to determine whether a specific substring is contained within the string. A Boolean value, true is returned if the substring is contained within the string; false otherwise. The Display name field appears with a default value. Edit the value if you want. Enter the Substring in the Substring field in the Method Arguments section.

    • String.endsWith to determine whether the string ends with a specific suffix. A Boolean value, true is returned if the string ends with the specified suffix; false otherwise. The Display name field appears with a default value. Edit the value if you want. Enter the suffix string in the Suffix field in the Method Arguments section.

    • String.replaceAll to replace each substring of this string that matches the given regular expression with the given replacement String. The Display name field appears with a default value. Edit the value if you want. Enter the regular expression and the replacement Strings in the Regex and Replacement fields of the Method Arguments section.

    • String.startsWith to determine whether the string starts with a specific prefix. A Boolean value, true is returned if the string starts with the specified prefix; false otherwise. The Display name field appears with a default value. Edit the value if you want. Enter the suffix string in the Suffix field in the Method Arguments section.

    • String.subString to obtain a specific substring within a given string. The substring is specified by a beginIndex (inclusive) and an endIndex (exclusive). The Display name field appears with a default value. Edit the value if you want. Enter the begin index in the Begin index field and end index in the End index field in the Method Arguments section.

    • String.toLowerCase to convert all of the characters in this String to lower case. The Display name field appears with a default value. Edit the value if you want.

    • String.toUpperCase to convert all of the characters in this String to upper case. The Display name field appears with a default value. Edit the value if you want.

    • String.trim to return a copy of the String, with leading and trailing whitespace omitted. The Display name field appears with a default value. Edit the value if you want.

    • XPath if your mapping requires an XPath transformation. The Display name field appears with a default value. Edit the value if you want. In the Method Arguments section, enter the expression manually or click Browse and select the name of the file that contains a definition of the XML structure (the drop-down arrow allows you to select the scope of the selection process). Click OK. The XPath Helper opens, showing the XML structure of the selected file in the left-hand pane. Build the desired XPath expression using the XPath Helper.

    • XML transformation if your mapping requires an XSLT transformation. The Display name field appears with a default value. Edit the value if you want. In the Method Arguments section, enter the expression manually or click Browse to locate the file of the stylesheet to use. You can also use the drop-down arrow to create a new stylesheet or select from the local file system or workspace.

    • XMLDecode if your mapping requires parsing information from an XML document, and you want to use Apama’s XMLDecode transformation which offers higher performance than XPath transformation. See XMLDecode for more information on XMLDecode properties. The Display name field appears with a default value. In the Method Arguments section:

      • In the Node path field, specify a valid node path. For example, /root/someelement[2]/text().
      • In the Properties field, specify the properties as property=value. If you want to specify multiple properties, specify the properties separated by a semicolon.
    • Convert To XML if you want to convert an event to XML, or to convert an event field that is an event type or sequence type to XML. The Convert to XML method uses convention-based mapping. See Using convention-based XML mapping with JMS messages for more information. To use convention-based XML mapping, the event type representing the XML document must be nested inside a parent event type, so ensure that such a parent event type has been created. Typically, the parent event type might have two fields, a string field representing the JMS destination, and an event field representing the root of the XML document. The Display name field appears with a default value.

      In the Method Arguments section:

      • Optionally, type a namespace for the generated XML document in the Namespace field. For example, http://www.example.com/myevent.
      • Optionally, type a namespace prefix for the generated XML document in the Prefix field. See Convention-based JMS message mapping example for more information.
      • By default the Include empty fields option is enabled. This specifies that empty XML nodes will be generated when empty EPL string fields are encountered within an Apama event. This option does not affect empty strings within a sequence of EPL strings. If you clear the check box to disable the option, empty XML nodes will not be generated. The expression that will be used in the mapping rule is displayed in the Expression value field.
  2. Click OK.

XMLDecode

The XMLDecode is similar to XML codec functionality intended to be used in the correlator JMS. The XMLDecode uses the Least Recently Used (LRU) cache to avoid repeating the decode when multiple rules are working off of the same XML source. For example, JMS body.

The XMLDecode supports the following properties:

  • skipNullFields - Default value is True.
  • trimXMLText - Default value is False.
  • generateTwinOrderSuffix - Default value is True.
  • generateSiblingOrderSuffix - Default value is False.
  • logFlattenedXML - Default value is False.
  • namespaceAware - Default value is False.
  • xmlField - This property needs to be set only if nested XML within a CDATA section needs to be parsed.
  • sequenceField
  • ensurePresent
  • separator
  • parseNode

See The XML codec IAF plug-in for a detailed description of XMLDecode properties.

For properties with multiple values, use a comma (,) to separate the values.

For multiple properties, use a semicolon (;) as a separator and the equals sign (=) to separate the key and value.

The XMLDecode functionality can be used in two ways:

Handling binary data

JUEL mapping expressions can use the following methods on binary data.

The byteArrayToBase64() method takes a byte array as the input parameter and returns the Base64 encoded string. Following is an example of mapping byte array data to a Base64-encoded string, which can be mapped to an Apama string field.

${byteArrayToBase64(jms.body.bytesmessage)}

The base64ToByteArray() method takes a Base64-encoded string as an input parameter and returns a byte array. For example:

${base64ToByteArray(apamaEvent['body'])}

The javaObjectToByteArray() method takes a serializable Java object as the input parameter and returns a serialized byte array. In the following example, the body of jms.objectmessage is serialized into a byte array, the byte array is then encoded into a Base64-encoded string, and that string can be mapped to a string field in an Apama event.

${byteArrayToBase64(javaObjectToByteArray(jms.body.objectmessage))}

The byteArrayToJavaObject() method takes a byte array as the input parameter and returns a deserialized Java object. In the following example, the string field of an Apama event, which is a Base64-encoded string, is converted into a byte array. The byte array is then deserialized into a Java object and can be mapped, for example, to the body of jms.objectmessage.

${byteArrayToJavaObject(base64ToByteArray(apamaEvent['body']))}

These binary methods can be used by creating custom expressions. In the Event Mappings tab, right-click a <Custom> node and select Add Node to display the Add Node dialog.

Note:

In these binary methods, if the argument passed to the method is null or empty then the method returns null. If a null value would be set for a field in an Apama event then the field is set to the default value for the field’s type, for example, a string field is set to the empty string, "".

Using custom EL mapping extensions

Apama’s correlator-integrated adapter for JMS uses an expression-based mapping layer to map between Apama events and external message payloads. The expressions use Java Unified Expression Language (EL) resolvers and methods, which must be registered to the mapping layer. Apama includes a set of EL resolvers and EL methods that are registered for you and that you can use in mapping expressions. If you want you can register your own EL resolvers and EL methods and then use them as custom mapping extensions.

See the ApamaDoc API reference information for details about the APIs mentioned in the following steps. An example that uses these APIs is in the samples\correlator_jms\mapping-extensions folder of your Apama installation directory.

To register and use custom mapping extensions

  1. Define a public class that imports com.apama.adapters.el.api.ELMappingExtensionProvider and com.apama.adapters.el.api.ELMappingExtensionManager.

  2. Implement ELMappingExtensionProvider.

  3. Override the ELMappingExtensionProvider.registerExtensions() method and register each custom EL method and each custom EL resolver with a call to ELMappingExtensionManager.registerMethod() or ELMappingExtensionManager.registerResolver(), as appropriate. For example:

    package com.apama.test;
    
    import com.apama.adapters.el.api.ELMappingExtensionManager;
    import com.apama.adapters.el.api.ELMappingExtensionProvider;
    
    public class MyStringMethods implements ELMappingExtensionProvider {
       // Register EL methods:
       @Override
       public void registerExtensions(ELMappingExtensionManager manager) {
          throws Exception {
             manager.registerMethod("reverse",
                     getClass().getMethod("reverse", String.class));
             manager.registerMethod("p:prefix",
                     getClass().getMethod("prefix", String.class, String.class));
       }
    
       public static String reverse(String str) {
          return new StringBuilder(str).reverse().toString();
       }
    
       public static String prefix(String str, String prefix) {
          if (str != null) {
             return prefix + str;
          } else {
             return prefix;
          }
       }
    }
    
  4. Register the list of mapping extension providers by adding a com.apama.adapters.el.config.ELMappingExtensionProviderList bean to the XML configuration, and setting its mappingExtensionProviders property. For example:

    <bean class="com.apama.adapters.el.config.ELMappingExtensionProviderList">
       <property name="mappingExtensionProviders">
          <list>
             <bean class="com.apama.test.MyStringMethods"></bean>
             <bean class="com.apama.test.MyIntegerMethods"></bean>
          </list>
       </property>
    </bean>
    

    The place to set this bean XML snippet is as follows: specify the com.apama.adapters.el.config.ELMappingExtensionProviderList bean in an existing spring XML file or in a separate file in the same location as other spring files. The recommended location is the jms-global-spring.xml file.

  5. Use mapping extensions in expressions inside the source expressions of mapping rules for both send and receive mappings.

    For example, consider a custom static method that takes a string parameter, returns the reverse string, and is registered with the name my:reverse. You can use it in a mapping rule as follows:

    <mapping:rule
     source="${**my:reverse\(**apamaEventType['test.MyMessage'].apamaEvent['body']**\)**}"
     target="${jms.body.textmessage}" type="BINDING_PARAM"/>
    

    In this example, my:reverse is applied to the expression "apamaEventType['test.MyMessage].apamaEvent['body']". This means that the value of the input parameter for the my:reverse method will be the value returned by the expression "apamaEventType['test.TextMessage'].apamaEvent['body']", which returns the value of the "body" field of the "test.MyMessage" event. The result is that the value of the source expression "my:reverse(apamaEventType['test.MyMessage'].apamaEvent['body'])" will be the reverse of the string contained in the "body" field.

    You can use Apama Plugin for Eclipse to add custom expressions to event mappings. In the Event Mappings tab of your adapter editor, right-click the <Custom> node and select Add Node. This displays the Add Node dialog, which prompts you to enter a custom expression.

  6. Ensure that the .jar file that contains your mapping extension providers is on the appropriate classpath.

    Use a <jms:classpath> element to enclose the ELMappingExtensionProviderList bean.

JUEL mapping expressions reference for JMS

The expressions that can be used to get or set elements of a JMS message are listed below, along with the set of Apama field types that are recommended for use when mapping when sending or receiving JMS messages:

JMS message element / JMS EL expression

Compatible Apama field type(s) when sending

Compatible Apama field type(s) when receiving

Dictionary of all message headers jms.headers

dictionary<string, string>

dictionary<string, string>

JMSDestination jms.header[​'JMSDestination']

string (with jndi:/topic:/queue: prefix)

string (with topic:/queue: prefix)

JMSReplyTo jms.header[​'JMSReplyTo']

string (with jndi:/topic:/queue: prefix)

string (with topic:/queue: prefix)

JMSCorrelationID jms.header[​'JMSCorrelationID']

string

string

JMSType jms.header[​'JMSType']

string

string

JMSPriority jms.header[​'JMSPriority']

integer, string

integer, string

JMSDeliveryMode jms.header[​'JMSDeliveryMode']

integer, string (must be a number (though display string can be used (only) when mapping a constant value in tooling); 1=NON_PERSISTENT, 2=PERSISTENT)

integer, string

JMSTimeToLive jms.header[​'JMSTimeToLive']

integer, string (in milliseconds from the time JMS sends the message)

N/A when receiving

JMSExpiration jms.header[​'JMSExpiration']

N/A when sending

integer, string (in milliseconds since the epoch)

JMSMessageID jms.header[​'JMSMessageID']

N/A when sending

boolean, string

JMSTimestamp jms.header[​'JMSTimestamp']

N/A when sending

integer, string (in milliseconds since the epoch)

JMSRedelivered jms.header[​'JMSRedelivered']

N/A when sending

string

Dictionary of all message properties jms.properties

dictionary<string, string>

dictionary<string, string>

String Message Property jms.property[​'propName']

string

string

Boolean Message Property jms.property[​'propName']

boolean

boolean, string

Long Message Property jms.property[​'propName']

integer

integer, string

Double Message Property jms.property[​'propName']

float

float, string

Byte Message Property jms.property[​'propName']

Not supported

string

Short Message Property jms.property[​'propName']

Not supported

string

Integer Message Property jms.property[​'propName']

Not supported

string

Float Message Property jms.property[​'propName']

Not supported

string

JMSX Property jms.xproperty[​'propName']

Same as other properties

Same as other properties

Dictionary of all JMSX properties jms.xproperties

dictionary<string, string>

dictionary<string, string>

TextMessage Body jms.body.textmessage

string, event [1]

string, event [1]

MapMessage Body jms.body.mapmessage

dictionary<string, string>

dictionary<string, string>

MapMessage Body Entry jms.body.mapmessage[​'mapKey']

string

string

ObjectMessage Body with a serializable java.util.Map <Object,Object> jms.body. objectmessage

dictionary<string, string>

dictionary<string, string>

ObjectMessage Body with a serializable java.util.List <Object> jms.body. objectmessage

sequence<string>

sequence<string>

ObjectMessage Body with any serializable Object jms.body. objectmessage

N/A

string

BytesMessage Body jms.body.bytesmessage

string, sequence<string>, dictionary<string, string>, event

string, sequence<string>, dictionary<string, string>

TextMessage, MapMessage, BytesMessage, ObjectMessage, Message jms.body.type

string

string

[1] If a string from the JMS message is mapped to an event, the string should be either of:
  • An Apama event string (as generated by the Apama Event Parser), whose event type matches the type of the field it is being mapped to in the source/target Apama event.
  • An XML document starting with a < character, whose structure matches what is implied by the event type definition it is being mapped to (see Using convention-based XML mapping with JMS messages for more information)

Note, the JMS headers JMSMessageID, JMSRedelivered and JMSDeliveryMode are supported for completeness but will not normally be required by Apama applications, since built-in duplicate detection based on application-level unique identifiers replaces the first two, and rather than overriding the per-message delivery mode it is usually best to use the default PERSISTENT/NON_PERSISTENT setting implied by the sender’s senderReliability value.

Resolver expressions for obtaining ids

The following tables describe resolver expressions for obtaining sender, receiver, and connection IDs. You cannot use these expressions to set ids.

For sending messages Description
${jmsSender['senderId']} Get the sender id of the sender that is sending the event from your Apama application to a JMS broker.
${jmsSender['connectionId']} Get the connection id of the sender that is sending the event from your Apama application to a JMS broker.
For receiving messages Description
${jmsReceiver[‘receiverId']} Get the receiver id of the receiver in your Apama application that received the JMS message from a JMS broker.
${jmsReceiver['connectionId']} Get the connection id of the receiver in your Apama application that received the JMS message from a JMS broker.

String methods in mapping expressions

In JUEL mapping expressions, you can use certain string methods in the parts of the mapping expressions that evaluate to string types. The table below describes the string methods you can use. These methods use the same-named java.lang.String methods. The mapping expressions are evaluated first to obtain a result string and then any specified string method is applied. You use these functions in the following way:

${*some_expression*.substring(5)}

In the previous format, *some_expression* is an expression that evaluates to a string. In the following examples, f1 is a field of type string:

${apamaEvent['f1'].toString().contains('in')}
${jms.body.textmessage.toString().startsWith('sample')}

String method

Description

equalsIgnoreCase('str')

Returns a boolean value that indicates whether the result string is equal to the specified string, ignoring case.

contains('str')

Returns a boolean value that indicates whether the result string contains the specified string.

matches('regex')

Returns a boolean value that indicates whether the result string matches the specified Java regular expression.

startsWith('str')

Returns a boolean value that indicates whether the result string starts with the specified string.

endsWith('str')

Returns a boolean value that indicates whether the result string ends with the specified string.

toLowerCase()

Converts the result string to lowercase and returns it.

toUpperCase()

Converts the result string to uppercase and returns it.

concat('str')

Appends the result string with the specified string and returns this result.

replaceAll('regex','regexReplacement')

In the result string, for each substring that matches regex, this method replaces the matching substring with regexReplacement. The string with replacement values is returned.

The regexReplacement string may contain backreferences to matched regular expression subsequences using the \ and $ characters, as described in the Java API documentation for java.util.regex.Matcher.replaceAll(). If a literal $ or \ character is required in regexReplacement be sure to escape it with a backslash, for example: "\$" or "\\".

substring(startIndex,endIndex)

Returns a new string, which is a substring of the result string. The returned substring includes the character at startIndex and subsequent characters up to but not including the character at endIndex.

substring(startIndex)

Returns a new string, which is a substring of the result string. The returned string includes the character at startIndex and subsequent characters including the last character in the string.

trim()

Returns a copy of the result string with leading and trailing whitespace removed.

## Binary methods in mapping expressions

In JUEL mapping expressions, you can use certain binary methods in the parts of the mapping expressions that evaluate to binary data. The table below describes the binary methods you can use.

Binary method Description
byteArrayToBase64(byteArray) Encodes a byte array to a Base64-encoded string.
base64ToByteArray(string) Decodes a Base64-encoded string to a byte array.
javaObjectToByteArray(object) Serializes the serializable Java object to a byte array.
byteArrayToJavaObject(base64String) Deserializes the byte array to a serializable Java object.

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.

Specifying transformation types

In the Mapping Element Details section, in the Transformation Type field select the desired type from the drop-down list.

To specify the transformation details

  1. In the Mapping Definitions section, draw the line indicating the mapping from source to target.

  2. In the Mapping Definitions section, click on the line that specifies the mapping rule.

  3. Under Mapping Element Details on the Properties tab, in the Transformation Type drop-down list:

    • Select XSLT Transformation. In the Stylesheet URL field, click Browse to locate the stylesheet file.

    • Select XPath. In the XPath Expression field, specify a valid XPath expression. You can either enter the XPath expression directly or you can use the XPath builder tool to construct an expression. To use the XPath Builder:

      1. Click the Browse button […] to the right of the XPath Expression field.
      2. In the Select input for XPath helper dialog, click Browse […] and select the name of the file that contains a definition of the XML structure (the drop-down arrow allows you to select the scope of the selection process). Click OK. The XPath Helper opens, showing the XML structure of the selected file in the left pane. See also Using the XPath Helper.
      3. In the XPath Helper, build the desired XPath expression by double-clicking on nodes of interest in the left pane. The resultant XPath expression appears in the XPath tab in the upper right pane. If the XML document makes use of namespaces, change the namespace option from Prefix to Namespace or Local name.
      4. In the XPath Helper, click OK. The XPath Builder closes and the XPath Expression field displays the XPath expression you built.
    • Select XMLDecode.

      1. In the Node path field, specify a valid node path.
      2. In the Properties field, specify the properties as property=value. If you want to specify multiple properties, specify the properties separated by a semicolon. For more information, see Specifying XML codec properties.