On the previous post I mentioned some pros for decoupling programs. One way of doing this is by using messages and message queues. One program puts a message on a queue and another program receives that message and thus informing the program that it has to do something. There are many middleware software packages which support this.

One of those packages is Apache Artemis. It supports one-to-one connections (queues) but also one-to-many (topics). The good thing about Apache Artemis is that it is written in Java and thus can run on IBM i. Just download the archive to your IBM i and extract the content of the archive (f. e. with unzip apache-artemis-2.31.0-bin.zip or jar -xf apache-artemis-2.31.0-bin.zip).

I like to place the software I manually install on the IFS on IBM i in the /opt folder. In addition I like to create a symlink (ln -s ...) so that it is easier to navigate the folders (but that is just a personal habit ;-) ). So my folder structure looks like this:

1
2
/opt/apache-artemis-2.27.1
/opt/artemis -> apache-artemis-2.27.1

Apache Artemis has a very good documentation and they explain everything in detail. Thus here an excerpt from it explaining how to install a new Apache Artemis broker instance.

The ActiveMQ Artemis broker follows a different paradigm where the project distribution serves as the broker “home” and one or more broker “instances” are created which reference the “home” for resources (e.g. jar files) which can be safely shared between broker instances. Therefore, an instance of the broker must be created before it can be run. This may seem like an overhead at first glance, but it becomes very practical when updating to a new Artemis version for example.

To create an Artemis broker instance navigate into the Artemis home folder and run: ./bin/artemis create /path/to/myBrokerInstance on the command line.

Let us place the Artemis broker instances at /var/local/lib/artemis/<broker>. The broker will manage auction messages so my command line looks like

1
./bin/artemis create /var/local/lib/artemis/auction

Now I could run this Artemis instance by executing /var/local/lib/artemis/auction/bin/artemis run. But first I need to configure some things. All configuration files of an Artemis instance can be found at the instance location in the folder etc.

1
2
3
4
5
6
7
8
9
10
mihael@ibmi:~/auction-broker/etc> ls -1
artemis-roles.properties
artemis-users.properties
artemis.profile
bootstrap.xml
broker.xml
jolokia-access.xml
log4j2.properties
login.config
management.xml

Acceptors

Acceptors are the things that make the network connections to the clients and receive/distribute messages. Artemis supports many protocols. But the protocol we are interested in is STOMP. Artemis can be configured to have many acceptors listening on different ports but also having one acceptor accepting multiple protocols listening on one port.

Note: As I need some “special” connection attributes for the local STOMP acceptor I go for one acceptor for local STOMP connections and one acceptor for everything else.

Here is the acceptor configuration in broker.xml.

1
2
3
4
5
6
7
8
9
10
<acceptors>
<!-- Acceptor for every supported protocol available locally and on the network -->
<acceptor name="artemis">tcp://0.0.0.0:40001?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;amqpMinLargeMessageSize=102400;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;amqpCredits=1000;amqpLowCredits=300;amqpDuplicateDetection=true;supportAdvisory=false;suppressInternalManagementObjects=false</acceptor>

<!--
STOMP acceptor only available on localhost with extra long connection timeout.
connection TTL: 60 * 60 * 24 * 1000 = 86400000 milliseconds = one day
-->
<acceptor name="stomp">tcp://localhost:40002?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=STOMP;useEpoll=true;connectionTtl=86400000</acceptor>
</acceptors>

Note: The connection TTL attribute is set because the RPG STOMP service program does not support sending heart beats at the moment and Apache Artemis does not accept having no heart beat (and I wouldn’t either ;-) ) so we are setting a very high connection TTL so that it is guaranteed that some event will be sent in this time frame (f. e. a day or a week).

Topic

Queues and topics can be automatically created but I also can specify a persistent topic in the configuration.

broker.xml
1
2
3
4
5
6
7
8
9
<addresses>
<address name="auction-events">
<multicast>
<queue name="sink">
<durable>true</durable>
</queue>
</multicast>
</address>
</addresses>

Here we have a topic auction-events and even if there is no subscriber to that topic all messages will at least be distributed to the queue sink which is really convenient in the development phase. So even if nobody subscribed to auction-events we will still see the incoming messages in the queue sink.

Logging

From the documentation:

Apache ActiveMQ Artemis uses the SLF4J logging facade for logging, with the broker assembly providing Log4J2 as the logging implementation. This is configurable via the log4j2.properties file found in the broker instance etc directory, which is configured by default to log to both the console and to a file.

I couldn’t have written it better.

For logging everything related to the STOMP acceptor just raise the log level of the StompConnection logger.

1
logger.org.apache.activemq.artemis.core.protocol.stomp.StompConnection.level=DEBUG

Note: You may need to also raise the level of the corresponding appender to DEBUG.

1
2
appender.console.filter.threshold.type = ThresholdFilter
appender.console.filter.threshold.level = DEBUG

Web Console

Apache Artemis comes out-of-the-box with a web console based on Hawtio. The only problem with this is that it is only available on the local machine. But just for giving this a try it would be easier if we could access it from outside localhost. For this we need to change two configuration entries.

bootstrap.xml
1
2
3
4
5
<web ... >
<binding name="artemis" uri="http://0.0.0.0:40000">
...
</binding>
</web>
jolokia-access.xml
1
2
3
4
<cors>
<allow-origin>*</allow-origin>
...
</cors>

Now we can access the Apache Artemis web console running on IBM i from our computer by accessing the URL http://my_ibmi:40000.

Large Text Messages

By default the web console will show you only the first 256 characters from a message. Depending on the project messages might be much larger (several thousand characters). The max. number of characters shown in the web console can be set in the broker.xml file.

broker.xml
1
2
3
4
5
6
7
<address-settings>
<address-setting>
...
<management-message-attribute-size-limit>50000</management-message-attribute-size-limit>
...
</address-setting>
</address-settings>

Here the max. number of displayed characters is set to 50.000.

Starting Artemis

Now I can run this Artemis instance by executing /var/local/lib/artemis/auction/bin/artemis run and have the log output in /var/local/lib/artemis/auction/log/artemis.log.

But there are other ways to start Artemis, f. e. via Service Commander.

SocketException on Startup

Depending on the network configuration of the IBM i server we may encounter the following error on startup:

1
java.net.SocketException: Protocol driver not attached

This error may occur on any product/project which utilizes Jetty. But we can work around this problem by specifying a Java system property. The startup script of Artemis supports adding Java system properties without having to modify the script by using the environment variable JAVA_ARGS_APPEND.

1
export JAVA_ARGS_APPEND="-Djava.net.preferIPv4Stack=true"

Now we can start Artemis as usual.

Service Commander

Service Commander is a tool for managing services and applications on IBM i. But in contrast to other tools Service Commander can manage services and applications of both worlds, PASE and native IBM i, and has a very nice integration with the native CLI by utilizing STRTCPSVR.

Service Commander stores the necessary data for each managed service in a stream file on the IFS, see folder .sc/services in the home folder. I placed the following configuration at /home/myuser/.sc/services/artemis.yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
name: Artemis
dir: /var/local/lib/artemis/auction
start_cmd: /var/local/lib/artemis/auction/bin/artemis run
stop_cmd: /var/local/lib/artemis/auction/bin/artemis stop

check_alive: 40000,40001,40002

batch_mode: yes
sbmjob_jobname: ARTEMIS
sbmjob_opts: JOBQ(QUSRNOMAX) USER(PM)

environment_vars:
- "JAVA_HOME=/QOpenSys/pkgs/lib/jvm/openjdk-11"
- "QIBM_MULTI_THREADED=Y"
- "JAVA_ARGS_APPEND=-Djava.net.preferIPv4Stack=true"

Now we have a whole bunch of possibilities for starting our Apache Artemis instance.

  1. From a SSH session: nohup /var/local/lib/artemis/auction/bin/artemis run > /dev/null 2> /dev/null &
  2. From a SSH session: sc start artemis
  3. From 5250 session: STRTCPSVR SERVICE(*SC) INSTANCE('artemis')

And now we can start it equally well from the PASE and the 5250 environment. And also no problems with having it autostarted after an IPL. Just add it to the autostart group in the Service Commander job definition YAML file.

Kudos to Jesse Gorzinski for this great project!

Happy integrating!

Mihael

PS: I mentioned “… written in Java … thus can run on IBM i.”. That is not totally correct. Because it is written in Java it can run in the Java VM on IBM i. But that does not guarantee that it will run correctly on IBM i. Many Java projects are done in mind with x86 as their target platform (or even only Windows) and thus it does not always work on IBM i. But so far I had luck with all of the Java projects I tried on IBM i. :-)