Subscribe to JMS Messages

IDM can subscribe to Java Messaging Service (JMS) messages using the Messaging Service's JMS Subscriber. In an event-driven architecture, also known as a message-driven architecture, there are publishers and subscribers. When a publisher sends a message over JMS, that message is broadcast. All active and subscribed clients receive that message. This sample shows how IDM can act as a JMS message subscriber, using the ActiveMQ JMS message broker.

Note

For more information on how IDM can publish JMS messages using the JMS Audit Event Handler, see Direct Audit Information to a JMS Broker

Sample Overview

With the scripted message handler shown in this sample, you can configure scripts to parse the contents of JMS messages, and act on that content.

The script in this sample, crudpaqTextMessageHandler.js, shows how JMS can handle ForgeRock REST operations. If you customize a script to manage JMS messages, you must also modify the conf/messaging.json file.

This sample uses ActiveMQ, a JMS message broker. With the ActiveMQ UI, you can act as the JMS message provider. This sample demonstrates how you can input REST payloads through the ActiveMQ UI.

Dependencies for JMS Messaging

The JMS audit event handler requires ActiveMQ, and a number of dependencies, that are bundled with the ActiveMQ delivery. This section lists the dependencies, and where they must be installed in the IDM instance. If you use a different ActiveMQ version, you might need to download the corresponding dependencies separately.

Download the following files:

  1. Unpack the ActiveMQ archive. For example:

    tar -zxvf ~/Downloads/apache-activemq-5.16.1-bin.tar
  2. Create a temporary directory, move the ActiveMQ Client, and bnd JAR files to that directory, then change to that directory:

    mkdir ~/Downloads/tmp
    mv ~/Downloads/apache-activemq-5.16.1/lib/activemq-client-5.16.1.jar ~/Downloads/tmp/
    mv biz.aQute.bnd-version.jar ~/Downloads/tmp/
    cd ~/Downloads/tmp/
  3. Create an OSGi bundle as follows:

    1. In a text editor, create a BND file named activemq.bnd and save it to the current directory. The file should have the following contents:

      version=5.16.1
      Export-Package: *;version=${version}
      Bundle-Name: ActiveMQ :: Client
      Bundle-SymbolicName: org.apache.activemq
      Bundle-Version: ${version}

      Your tmp/ directory should now contain the following files:

      ls ~/Downloads/tmp/
      activemq-client-5.16.1.jar	activemq.bnd			biz.aQute.bnd-version.jar
    2. In that same directory, create the OSGi bundle archive file as follows:

      java -jar biz.aQute.bnd-version.jar \
      wrap --properties activemq.bnd \
      --output activemq-client-5.16.1-osgi.jar \
      activemq-client-5.16.1.jar
  4. Copy the resulting activemq-client-5.16.1-osgi.jar file to the openidm/bundle directory:

    cp activemq-client-5.16.1-osgi.jar /path/to/openidm/bundle/
  5. Copy the required dependencies from the extracted ActiveMQ archive to the openidm/bundle directory:

    cp ~/Downloads/apache-activemq-5.16.1/lib/geronimo-j2ee-management_1.1_spec-1.0.1.jar /path/to/openidm/bundle/
    cp ~/Downloads/apache-activemq-5.16.1/lib/hawtbuf-1.11.jar /path/to/openidm/bundle/
    cp ~/Downloads/apache-activemq-5.16.1/lib/optional/jmdns-3.4.1.jar /path/to/openidm/bundle
    cp ~/Downloads/apache-activemq-5.16.1/lib/optional/commons-net-3.7.2.jar /path/to/openidm/bundle

Configure SSL for ActiveMQ

This configuration provides a connection to the ActiveMQ server instance with TLSv1.3.

  1. In the directory where you unpacked ActiveMQ, edit the conf/activemq.xml file as follows:

    • In the <brokers> element, add an <sslContext>:

      <broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}">
          ...
          <sslContext>
              <sslContext keyStore="file:${activemq.conf}/broker.ks" keyStorePassword="password"/>
          </sslContext>
          ...
      </broker>
    • In the <transportConnectors> element, add an ssl <transportConnector>:

      <transportConnectors>
          ...
          <transportConnector name="ssl" uri="ssl://0.0.0.0:61617?transport.needClientAuth=false"/>
      </transportConnectors>

      Note

      To enable mutual authentication, set transport.needClientAuth=true, and import the IDM server certificate into the ActiveMQ truststore (conf/broker.ts).

  2. Delete the existing self-signed server certificate from the ActiveMQ keystore and truststore:

    keytool \
    -delete \
    -keystore /path/to/activeMQ/conf/broker.ts \
    -alias broker-localhost
    Enter keystore password: password
    keytool \
    -delete \
    -keystore /path/to/activeMQ/conf/broker.ks \
    -alias broker-localhost
    Enter keystore password: password
  3. Generate a new self-signed server certificate for ActiveMQ:

    keytool \
    -genkey \
    -keyalg RSA \
    -alias broker-localhost \
    -keystore /path/to/activeMQ/conf/broker.ks \
    -storepass password \
    -validity 360 \
    -keysize 2048

    Important

    The CN in the generated self-signed certificate must match the hostname that you specify in the IDM JMS provider URL. If you are using localhost to connect to the broker, you must specify localhost when keytool prompts you for the first and last name. If the CN is not the same as the hostname, the server certificate validation will fail.

  4. Export the ActiveMQ server certificate:

    keytool \
    -export \
    -alias broker-localhost \
    -file broker-localhost.key \
    -keystore /path/to/activeMQ/conf/broker.ks
    Enter keystore password: password
    Certificate stored in file <broker-localhost.key>
  5. Import the ActiveMQ server certificate into the IDM truststore:

    keytool \
    -import \
    -alias activemq \
    -keystore /path/to/openidm/security/truststore \
    -file broker-localhost.key
    Enter keystore password: changeit
    Owner: CN=localhost, OU=Unknown, O=example.com, L=Unknown, ST=Unknown, C=Unknown
    Issuer: CN=localhost, OU=Unknown, O=example.com, L=Unknown, ST=Unknown, C=Unknown
    ...
    Trust this certificate? [no]: yes
    Certificate was added to keystore

Configure a Secure Port for JMS Messages

Edit /path/to/openidm/samples/scripted-jms-subscriber/conf/messaging.json, as follows:

"java.naming.provider.url" : "ssl://localhost:61617?daemon=true"

Start the ActiveMQ Broker and IDM

With the appropriate bundles in the /path/to/openidm/bundles directory, you're ready to start the ActiveMQ message broker, as well as IDM with the JMS Audit Sample.

  1. Navigate to the directory where you unpacked the ActiveMQ binary and run the following command to start the ActiveMQ broker:

    cd ~/Downloads/apache-activemq-5.16.1
    bin/activemq start
    INFO: Loading '/path/to/Downloads/apache-activemq-5.16.1/bin/env'
    INFO: Using java '/usr/bin/java'
    INFO: Starting - inspect logfiles specified in logging.properties and log4j.properties to get details
    INFO: pidfile created : '/path/to/Downloads/apache-activemq-5.16.1/data/activemq.pid' (pid '69627')
  2. Start IDM, with the configuration for this sample:

    cd /path/to/openidm/
    ./startup.sh -p samples/scripted-jms-subscriber
  3. Go to the ActiveMQ Console at http://localhost:8161/admin/topics.jsp, and verify audit messages are being created and sent to the forgerock.idm.audit audit topic.

Access the REST Interface Using the ActiveMQ UI

In this section, you will run REST calls through the ActiveMQ UI. This assumes you started ActiveMQ and IDM in "Start the ActiveMQ Broker and IDM".

  1. Access the ActiveMQ web console, at http://localhost:8161/admin. Log in as an administrator; the default administrative user and password are admin and admin.

  2. In the ActiveMQ web console, in the Queue Name text box, enter the value of idmQ from the conf/messaging.json file. The default value for queue.idmQ and destinationName is idmQ. Enter that value and select Create.

  3. In the Queues window for the idmQ queue, select the Send To link under Operations.

  4. You should see a Send a JMS Message window, with a Message body text box towards the bottom.

  5. Copy the following text, a payload to create a new user with a user ID of mgr1, to the Message body text box:

    {
      "operation" : "CREATE",
      "resourceName" : "/managed/user",
      "newResourceId" : "mgr1",
      "content" : {
        "mail" : "mgr1@example.com",
        "sn" : "Sanchez",
        "givenName" : "Jane",
        "password" : "Password1",
        "employeenumber" : 100,
        "accountStatus" : "active",
        "roles" : [ ],
        "userName" : "mgr1"
      },
      "params" : {},
      "fields" : [ "*", "*_ref" ]
    }

    For comparison, note the following equivalent REST call to create the same user:

    curl \
    --header "X-OpenIDM-Username: openidm-admin" \
    --header "X-OpenIDM-Password: openidm-admin" \
    --header "Accept-API-Version: resource=1.0" \
    --header "Content-Type: application/json" \
    --request POST \
    --data '{
      "mail" : "mgr1@example.com",
      "sn" : "Sanchez",
      "givenName" : "Jane",
      "password" : "Password1",
      "employeenumber" : 100,
      "accountStatus" : "active",
      "roles" : [ ],
      "userName" : "mgr1",
      "params" : {},
      "fields" : [ "*", "*_ref" ]
    }' \
    "http://localhost:8080/openidm/managed/user?_action=create"
  6. Observe the OSGi console. You should see output in two parts. The first part starts with:

    *****request received******
    Parsed JMS JSON =
    ... 

    The first part should include a JSON-formatted replay of the request that you entered in the Message body text box.

    The second part of the output includes information for that same user, as written to the managed user repository.

  7. Confirm that user either in the Admin UI, or using the following REST call:

    curl \
    --header "X-OpenIDM-Username: openidm-admin" \
    --header "X-OpenIDM-Password: openidm-admin" \
    --header "Accept-API-Version: resource=1.0" \
    --request GET \
    "http://localhost:8080/openidm/managed/user/mgr1"
  8. Repeat the process to create additional users. Try a different REST function. For example, you can enter the following payload in the ActiveMQ UI Message body text box, to change the first name (givenName) of the mgr1 user to Donna:

    {
      "operation": "PATCH",
      "resourceName": "/managed/user/mgr1",
      "value": [
        {
          "operation": "replace",
          "field": "/givenName",
          "value": "Donna"
        }
      ]
    }

Customize the Scripted JMS Sample

If you set up a custom script to parse and process JMS messages, store that script in the script/ subdirectory. Assume that script is named myCustomScript.js.

Edit the messaging.json file in the conf/ subdirectory, and point it to that file:

{
  "subscribers" : [
    {
      "name" : "IDM CREST Queue Subscriber",
      "instanceCount": 3,
      "enabled" : true,
      "type" : "JMS",
      "handler" : {
        "type" : "SCRIPTED",
        "properties" : {
          "script" : {
            "type" : "text/javascript",
            "file" : "myCustomScript.js"
          }
        }
      },
      "properties" : {
        "sessionMode" : "CLIENT",
        "jndi" : {
          "contextProperties" : {
            "java.naming.factory.initial" : "org.apache.activemq.jndi.ActiveMQInitialContextFactory",
            "java.naming.provider.url" : "tcp://127.0.0.1:61616?daemon=true",
            "queue.idmQ" : "idmQ"
          },
          "destinationName" : "idmQ",
          "connectionFactoryName" : "ConnectionFactory"
        }
      }
    }
  ]
}

You'll find some of these properties in "JMS Audit Event Handler Properties". Despite the name of the table and the different configuration file, the properties are the same.

Other properties of interest in the messaging.json file are shown in the following table:

JMS messaging.json Configuration Properties
messaging.json File LabelDescription
subscribersNeeded to subscribe to incoming JMS message requests.
nameArbitrary name for the subscriber.
instanceCountEach instanceCount manages a single connection between IDM and the messaging channel. Supports multithreading throughput. If subscribing to a queue, such as queue.idmQ, the message is handled by a single instance. If subscribing to a topic, all instances receive and handle the same message.
handlerParses the JMS message, then processes it, possibly through a script.
queue.idmQOne of the JNDI context properties. Name of the JMS queue in the ActiveMQ UI.
destinationNameJNDI lookup name for message delivery.

Read a different version of :