Working with the Java Messaging Service

Appian supports the Java Messaging Service (JMS) for sending and receiving events. JMS is an enterprise messaging standard that is supported by EJB2 (and later) application servers, including all application servers supported by Appian.

Configuring Java Messaging

In order to use the Java Messaging Service, your application server must have a JMS queue configured and named jms/AppianProcessIntegrationQueue. In order for Appian to receive the JMS messages, they must be placed on this queue.

Appian supports interoperability with the JMS provider included in the supported version of the application server listed below (e.g., HornetQ in JBoss EAP 6.x).

WebLogic

For a WebLogic application server, see Configuring WebLogic.

JBoss EAP

For a JBoss EAP application server, this is handled by the configure script.

None

If no queue is configured, JMS capabilities are disabled.

JMS Source Configuration

To set up Appian as a source for JMS messages, the jmsInstalled property must be set to true.

  • In Appian, the jmsInstalled property is set to true by default.
  • Upgraded systems that have merged configurations should check this value.

If JMS message destinations are not visible from the Send Message Event properties dialog box in the Process Modeler, set the following parameter in the custom.properties file:

1
conf.jms.jmsInstalled=true  

Authentication

Creating the JMS connection can require a username and password. (Not an Appian username/password. Instead this is a username/password specific to the JMS provider in the application server.) If the JMS connection requires a username/password, then set the following in the custom.properties file:

1
conf.jms.createConnectionUsername=<USERNAME> 

Create or update passwords.properties and add the following:

1
conf.password.JMS==<PASSWORD> 

These configurations determine the credentials used by Appian to connect to the JMS provider using the createConnection method.

  • If both are left blank, then the default createConnection method is used instead of the credentialed version. (As the JMS provider is running inside the application server, blank is typically the desired value.)

Receiving JMS Messages

Sending a JMS message to Appian from an external source involves the following:

  • Configuring Java Messaging (as described below).
    • The JmsProcessIntegrationHandlerMDB is a small Message Driven Bean (a type of Enterprise Java Bean managed by the application server).
    • In JBoss, the JmsProcessIntegrationHandlerMDB bean appears as jboss.j2ee as ear=suite.ear,jar=JmsProcessIntegrationHandlerBean,service=EJB3 in the JMX view. The JMX view exposes control methods for the EJB, such as stop and start.
    • The JmsProcessIntegrationHandlerMDB bean is configured to listen for messages on a point-to-point queue named jms/AppianProcessIntegrationQueue. (This queue may be listed as simply AppianProcessIntegrationQueue under queues in some application servers).
  • Creating messages.
  • Populating messages with the data you intend to process.
  • Sending the message.
  • Committing the send either explicitly (using commit) or implicitly (using close).
  • Using a Receive Message Event in your process models to expose a Java message to your process.
    • Messages are routed to a process model, process, or particular event within a process model.
    • This routing data is encoded within the message headers.

Routing JMS Messages

JMS messages sent to Appian can be routed to the following process entities:

Process Model

  • Messages can be routed according to process model UUID or process model ID.
  • The process model must be published. Only the latest published model receives messages.
  • The process model must have a Receive Message Event trigger configured for the Start Event (for non-running processes).
  • Each running instance of the process model (with one or more active Receive Message Events) gets the message. The process must be active (neither paused nor paused by exception).
  • Each active Receive Message Event in the process receives the message (additional filtering can be specified at the node level).

Process Instance

  • Routed using the process ID.
  • The process must be active (neither paused nor paused by exception).
  • Each active Receive Message Event in the process gets the message (additional filtering can be specified at the node level).

Receive Message Event

  • JMS messages can be routed to an event using a persistent ID.
  • The process instance containing the event must be active (it cannot be paused nor paused by exception).
  • The Receive Message Event node must be active.

JMS Message Interfaces

All JMS Message Interfaces can be received by Appian, but only the TextMessage has special support. The getText/setText interface is supported as the body from the user interface.

Handling Complex Data in a JMS Message

JMS header properties support only simple properties, not arrays.

To handle complex data and arrays, the routing data in the message header must have complex data externalized. The setExternalizedProperty method of the InternalMessage class (com.appiancorp.suiteapi.messaging.InternalMessage) is used by Appian to encode data (particularly arrays) such that they appear to be strings to the surrounding system (particularly JMS). These routing methods are detailed below.

Appian receives the messages via its JmsProcessIntegrationHandlerMDB Message Driven Bean (MDB), which is a type of Enterprise Java Bean (EJB).

Routing Messages Sent by Appian to Appian

InternalMessage.routeToProcessModel (UUID or ID)

To send a message to a process model by UUID, see the InternalMessage.routeToProcessModel(Message, String, String) method.

To send to a process model by process model ID, see the InternalMessage.routeToProcessModel(Message, Long, String) method.

A message routed to a process model is only received by an active process or a process model Start Event. The process must listen for the message.

  • A message received before the process is listening is ignored.
    For example, if the intended recipient of a message is a Receive Event in your process, then a flow must activate the Receive Event, which must remain active (a flow token must remain at the event). If the intended recipient is the Start Event of a process model, then the process model must be saved and published.

A message routed to a process model triggers all active Receive Events in the process model.

  • A filter within the event can be used to limit the messages that are handled by the event.

The process model UUID of a saved process model is displayed on the Process Model Properties dialog box. (Before the process model is saved, an Unsaved Process Model message appears). Once saved, the process model UUID is displayed on the General tab of the Process Model Properties dialog box.

  • This UUID value would be used for targeting a message.

NOTE: We recommend using the UUID for targeting messages, as it remains the same once the process model is exported from a development system and imported onto a production system. The process model ID likely differs between development and production. The process model ID can be used to send messages to process models created programmatically, where a process model can be imported and then immediately receive a message.

Message Headers set with Process Model UUID Targeting

The following message headers are set when targeting a process model by UUID:

1
2
3
DESTINATION_PMUUID_KEY	Process Model UUID (externalized)
JMS_TYPE_KEY			EXTERNAL_TO_PROCESS
USERNAME_KEY			username

- OR -

1
2
3
4
5
6
7
8
9
10
DESTINATION_PROCESS_MODEL_ID_KEY	Process Model ID (externalized)
JMS_TYPE_KEY					EXTERNAL_TO_PROCESS
USERNAME_KEY					username
Where:
  
DESTINATION_PMUUID_KEY is "DestinationPMUUID"
DESTINATION_PROCESS_MODEL_ID_KEY is "DestinationPMID"
JMS_TYPE_KEY is "JMSType"
USERNAME_KEY is "UserName"
EXTERNAL_TO_PROCESS is "External to Process"

InternalMessage.routeToEventPersistentId (UUID)

To send a message to a process model or the persistent ID of a Receive Message Event in a process (visible in the Edit Receive Message Event Properties dialog box for either Start or Intermediate Events), see the InternalMessage.routeToEventPersistentId(Message, String, String) method.

A message routed to an event persistent ID is only received by a node within an active process or the Start Event of a process model. It must already be listening for the message.

A message received by the process engine before it is listening is ignored. If the intended recipient is a Receive Event, then a flow must have activated that Receive Event and the Receive Event must still be active. If the intended recipient is the Start Event of a process model, then the process model must be saved and published.

A message routed to the process model triggers all matching nodes.

  • A message filter within the node can be used to narrow the reception.

The event’s persistent ID is displayed on the General tab of the message event’s dialog box. Until the process model is saved, this ID is not set. An Event Not Saved message is displayed instead. Once the process model is saved, the event’s persistent ID is displayed.

  • This ID value would be used for targeting a message.

Message Headers set with Persistent Event ID Targeting

The following message headers are set when targeting a process model or Receive Message Event in a process according to its persistent ID:

1
2
3
DESTINATION_EVENT_PERSISTENT_ID_KEY	Event UUID (externalized)
JMS_TYPE_KEY					EXTERNAL_TO_PROCESS
USERNAME_KEY					username

Where:

1
2
3
4
DESTINATION_PMUUID_KEY is "DestinationEventPersistentID"
JMS_TYPE_KEY is "JMSType"
USERNAME_KEY is "UserName"
EXTERNAL_TO_PROCESS is "External to Process"

InternalMessage.routeToProcess (ID)

A message can be sent to a process using the process ID using the InternalMessage.routeToProcess(Message, Long, String) method.

A message routed to a process is only received by an active process (neither paused by an Administrator nor paused by exception).

  • It must be listening for the message. A message received by the process engine before it is listening is ignored.
    • If the intended recipient is a Receive Event, then a flow must have activated that Receive Event, and the Receive Event must still be active.

A message routed to a process triggers all active Receive Events in the process.

  • A message filter within the node can be used to narrow the reception.

Sending a message to a process ID may be useful if you are programmatically starting processes. Upon starting a process from a process model, the process ID is returned as a Long.

  • This value can be used for sending a message to the process.

The process ID of a long running process can also be obtained by a report.

  • Since The process ID only lives as long as the process, do not hard-code process IDs.
  • If you must use the ID, include a mechanism to update it.

Message Headers Set with Process ID Targeting

The following method can be used to set a Process ID in the message headers:

1
2
3
DESTINATION_PROCESS_ID_KEY		Process ID (externalized)
JMS_TYPE_KEY					EXTERNAL_TO_PROCESS
USERNAME_KEY					username

Where:

1
2
3
4
DESTINATION_PROCESS_ID_KEY is "DestinationProcessID"
JMS_TYPE_KEY is "JMSType"
USERNAME_KEY is "UserName"
EXTERNAL_TO_PROCESS is "External to Process"

Code Example for a Message Intended to be Received by the Appian JMS Process Queue

The following example sends a message to the process model with the UUID stored in PROCESS_MODEL_UUID.

If the model with the given UUID has a Start Event listening for events External-to-Process, a process is launched based on the receipt of the message.

Other route convenience methods are listed below, showing how to route JMS messages to processes, process models, and particular nodes within a process or process model.

The connection information given in this sample is particular to JBoss, but the routing methods are generic for all JMS implementations.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
package com.example.appian;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import com.appiancorp.suiteapi.messaging.InternalMessage;
import com.appiancorp.suiteapi.process.TypedVariable;
import com.appiancorp.type.AppianTypeLong;

public class ExternalToProcessMessage {

  private static final String SECURITY_PRINCIPAL ="guest";
  private static final String SECURITY_CREDENTIALS ="guest1";

  private static final String PROCESS_MODEL_UUID = "0003d6f2-967e-8000-a2f0-010000010000";
  private static final String CONNECTION_FACTORY_NAME ="jms/AppianProcessIntegrationConnectionFactory";
  public static final String JMS_TYPE_KEY = "MessageType";
  public static final String EXTERNAL_TO_PROCESS = "External to Process";

  //Test send
  public void testSendToAEViaJBoss() throws Exception {
    sendToAE(getInitialContext_JBoss(), CONNECTION_FACTORY_NAME);
  }

  public static void main(String[] args) throws Exception{
    ExternalToProcessMessage e = new ExternalToProcessMessage();
    e.sendToAE(e.getInitialContext_JBoss(), CONNECTION_FACTORY_NAME);
  }

  //Set the context
  private Context getInitialContext_JBoss() throws NamingException {
    Properties prop = getProperties();
    prop.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.naming.remote.client.InitialContextFactory");
    prop.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming");
    prop.setProperty(Context.PROVIDER_URL, "remote://localhost:4447");

    return new InitialContext(prop);
  }

  public void sendToAE(Context context, String connectionFactoryName) throws Exception {
    ConnectionFactory connectionFactory = (ConnectionFactory) context.lookup(connectionFactoryName);
    Connection connection = connectionFactory.createConnection();
    Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
    Destination destination = (Destination) context.lookup("jms/AppianProcessIntegrationQueue");
    TextMessage message = session.createTextMessage();
    message.setStringProperty(JMS_TYPE_KEY, EXTERNAL_TO_PROCESS);
    message.setStringProperty("OriginIP", "127.0.0.1");

    message.setStringProperty("UserName","sathya" ); //this is appian user id
    message.setStringProperty("DestinationPMUUID", PROCESS_MODEL_UUID);
    message.setStringProperty("MessageType", "External to Process");

    message.setLongProperty("test", new Long(100));
    //message.setStringProperty("data1", "Data 1 value");
    //message.setBooleanProperty("data2", new Boolean(true));
    message.setText("This is the text of the optional body");
    MessageProducer messageProducer = session.createProducer(destination);
    messageProducer.send(message);
    messageProducer.close();
    session.close();
    connection.close();
  }

  //properties set
  private Properties getProperties() {
    Properties prop = new Properties();
    prop.put(Context.SECURITY_PRINCIPAL, SECURITY_PRINCIPAL);
    prop.put(Context.SECURITY_CREDENTIALS, SECURITY_CREDENTIALS);
    return prop;
  }
}

See Also

FEEDBACK