App Connect

App Connect

Join this online user group to communicate across IBM product users and experts by sharing advice and best practices with peers and staying up to date regarding product enhancements.

 View Only

ACE v12 with Java11 and Apache Qpid JMS

By Trevor Dolby posted Wed October 25, 2023 11:02 PM

  

App Connect Enterprise (ACE) 12.0.10 is the first release in quite a while to ship two Java versions, with Java11 in addition to Java8 for servers only. While running IntegrationServers with JREs later than Java8 has been technically possible for some time (see a v11 experiment here), ACE v12 is the first to officially support later Java versions, in order to enable capability such as the "Change Data Capture" node as described by Ben Thompson in his announcement blog. There are also other use cases that will benefit even though toolkit support is not yet available, and this article describes one such case: recent Apache Qpid JMS drivers no longer support Java8, so the ACE server needs to use a later JRE.

The applications and configurations used in this article are all available at https://github.com/trevor-dolby-at-ibm-com/ace-java11-qpid-demo and can be tried out independently and extended or modified.

Background

Java versions in ACE and predecessor products have normally been the same in all components, with the toolkit, commands, supporting processes, and servers all using the same Java version. This ensures that any Java code created by the toolkit can be executed by the servers, along with any additional libraries pulled in as part of Mapping or JavaCompute nodes. The fundamental flow and data parsing code is C++ (and always has been) along with quite a few message flow nodes (such as the MQ nodes), and v10 added in a node.js runtime for additional capabilities, so the current (simplified, with many details missing) picture looks as follows:

While Message Broker v6 did support and ship Java 1.4 and 1.5 at various points, this was unusual and the norm has been to use a single Java version. Regardless of how many Java levels are shipped, there is only one active Java Virtual Machine (JVM) running in a process at one time, and the choice of version is made at process start time.

Mapping, JavaCompute, and CICS nodes are not the only nodes that use Java, however, and the JMS nodes are of particular interest in this case: they dynamically load JMS driver libraries at runtime rather than requiring all the code to be present in the toolkit during development. A JMS-focused picture would look as follows, with the non-Java server components minimized:

JDBC works the same way with database drivers, so the same principle applies there as well: flows and code can be written in the toolkit using the standard ACE java classes, and the actual driver implementations are loaded at runtime. However, for both JDBC and JMS any attempt at loading drivers built with anything later than Java8 would result in errors if the server was running with Java8 as shown:

BIP2230E: Error detected whilst processing a message in node 'PutFlow.JMS Output'.
BIP4367E: The method 'evaluate' in Java node 'JMS Output' has thrown the following exception: java.lang.UnsupportedClassVersionError: JVMCFRE199E bad major version 55.0 of class=org/apache/qpid/jms/jndi/JmsInitialContextFactory, the maximum supported major version is 52.0; offset=6.

The reason for these errors is that the Java class file format created by different Java compiler versions is not forward-compatible: a newer Java runtime can run older Java classes, but an older Java runtime cannot run newer classes because it lacks the required functionality. The supported class file versions from the official Java docs are:

JDK Version Class File Versions Supported
Java 8 45 - 52
Java 9 45 - 53
Java 10 45 - 54
Java 11 45 - 55

where 45 is from Java 1.0.2 and the numbers continue on with future Java versions (Java20's version 64 is the latest at the time of writing).

These errors happen with Apache Qpid JMS v1 and v2 JARs, as anything later than v0.61.0 does not support Java8 (being built using the Java11 class file format). Version 0.61.0 was released in late 2021 and no new v0 release have been published since, so moving to v1/v2 is likely to be essential to avoid exposure to security CVEs from outdated prereqs.

JMS nodes and Java11

Avoiding UnsupportedClassVersionErrors requires the server to be running a Java11 or later JVM, but does not require any changes to existing JavaCompute nodes. The Java11 JVM will run code compiled with Java8 without any issues, as Java only prohibits running code compiled on newer Java levels: older Java code still runs as expected. This means that the server can be changed to use Java11 so that the JMS drivers can be loaded:

Enabling Java11 must happen before the server is started, using the following command (see docs here):

ibmint specify jre --version 11 --work-dir /home/aceuser/ace-server

This switches the Java version when the server is next started up, and the JMS drivers will be loaded as expected. Switching to Java11 does disable some other functionality that is not currently supported, mostly due to dependencies on aspects of the IBM Java8 implementation that have not been carried forward (such as JSSE2). The Mapping node is one example, and the list can be found here.

Example JMS flows, configuration, and demo container

The simplest demo uses an Apache Qpid broker as a pubsub server for JMS nodes running in ACE flows. The Qpid JMS client must be configured with a JMSProviders policy and appropriate JNDI details, and the demo will use the provided topic amq.topic to avoid needing to configure the broker at all:

ACE with Qpid broker


The publisher flow will run using a timer to avoid any need to trigger the flow, and the subscriber flow will simply print received messages to the console. Assuming a successful run, the output will look something like this:

2023-10-24 02:38:53.206660: BIP4649I: A connection with the JMS provider has been made on behalf of node ComIbmJMSClientOutputNode using initial context factory org.apache.qpid.jms.jndi.JmsInitialContextFactory, JNDI bindings location /tmp/apache-qpid-jndi.properties and connection factory name demoCF. This connection is using the JMS transaction mode None.
2023-10-24 02:38:53.206768: BIP4649I: A connection with the JMS provider has been made on behalf of node ComIbmJMSClientInputNode using initial context factory org.apache.qpid.jms.jndi.JmsInitialContextFactory, JNDI bindings location /tmp/apache-qpid-jndi.properties and connection factory name demoCF. This connection is using the JMS transaction mode None.
2023-10-24 02:38:57.409     43 ========= JMSSender sending a message to a topic ========

2023-10-24 02:38:57.434524: BIP8099I: JMSReceiver received message:
( ['json' : 0x7fe7501a92c0]
  (0x01000000:Object):Data = (
    (0x03000000:NameValue):hello = 'world' (CHARACTER)
  )
)
: GetFlow.Trace  -  {2}

with the trace output showing the JSON data sent from the publisher flow via the Qpid broker.

Note that this demo only works with v1 Qpid JMS client JARs if the server has been configured to use Java11, as otherwise UnsupportedClassVersionErrors will occur as shown above.

The repo at https://github.com/trevor-dolby-at-ibm-com/ace-java11-qpid-demo contains a Dockerfile that allows the demo to be built and run as a container, with the Qpid broker and JMS client downloaded as part of the container build. The resulting container will automatically start the broker alongside the IntegrationServer to allow the flows to run:

ACE demo container


To see what happens when using Java8, the lines in the Dockerfile that run ibmint specify jre can be commented out:

RUN bash -c "export LICENSE=${LICENSE} ; . /opt/ibm/ace-12/server/bin/mqsiprofile ; \
    ibmint specify jre --version 11 --work-dir /home/aceuser/ace-server"

and the UnsupportedClassVersionErrors will be seen:

2023-10-24 01:58:25.228930: BIP2230E: Error detected whilst processing a message in node 'PutFlow.JMS Output'.
2023-10-24 01:58:25.228966: BIP4367E: The method 'evaluate' in Java node 'JMS Output' has thrown the following exception: java.lang.UnsupportedClassVersionError: JVMCFRE199E bad major version 55.0 of class=org/apache/qpid/jms/jndi/JmsInitialContextFactory, the maximum supported major version is 52.0; offset=6.
2023-10-24 01:58:25.229000: BIP4395E: Java exception: 'java.lang.UnsupportedClassVersionError'; thrown from class name: 'java.lang.ClassLoader', method name: 'defineClassImpl', file: 'ClassLoader.java', line: '-2'
2023-10-24 01:58:25.229076: BIP4367E: The method 'defineClassImpl' in Java node 'unknown' has thrown the following exception: java.lang.UnsupportedClassVersionError: JVMCFRE199E bad major version 55.0 of class=org/apache/qpid/jms/jndi/JmsInitialContextFactory, the maximum supported major version is 52.0; offset=6

Other possibilities with Java11 and later

JDBC is another desirable use case, and would follow the same pattern as JMS above.

Toolkit support for Java11 or later is clearly desirable, but no date for this has been announced by ACE development. Switching the ibmint command to use Java11 is possible in theory, and an experimental minimal container that does this can be found here, leaving the picture as follows:


However, this still does not allow JCN writers to use Java11 or later code (such as isBlank() for java.lang.String) because the toolkit would flag such code as invalid and refuse to compile the project. This would make it impossible to run unit tests, for example, so there is little value in simply enabling the pipeline tools for Java11 without also having toolkit support.

Summary

ACE 12.0.10's support for Java11 enables important use cases in addition to the "Change Data Capture" node even though toolkit support is not available yet, and the Apache Qpid JMS client demo (repo here) shows how this can be used right now. Further enhancements would make this even more useful, such as toolkit support and Java17 enablement, but the existing capability is useful today in many ways.

0 comments
42 views

Permalink