I have run into an issue where the Actuator probe fails for JMS health even though my routes can connect and produce message to JMS. So in short Actuator is saying it is down but it is working.
Tech stack and tech notes:
- Spring-boot: 2.3.1.RELEASE
- Camel: 3.4.1
- Artemis: 2.11.0
- Artemis has been setup to use a user name and password(artemis/artemis).
- Using
org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory
for connection factory.
My route is as simple as chips:
<route id="timer-cluster-producer-route">
<from uri="timer:producer-ticker?delay=5000"/>
<setBody>
<groovy>
result = ["Name":"Johnny"]
</groovy>
</setBody>
<marshal>
<json library="Jackson"/>
</marshal>
<to uri="ref:jms-producer-cluster-event" />
</route>
XML Based Artemis Configuration
With Spring-boot favoring java based configuration I am busy migrating our XML beans accordingly.Thus I took a working beans.xml file pasted into the project and fired up the route and I could send messages flowing and the health check returned OK.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd">
<bean id="jmsConnectionFactory"
class="org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
<property name="user" value="artemis"/>
<property name="password" value="artemis"/>
<property name="connectionLoadBalancingPolicyClassName" value="org.apache.activemq.artemis.api.core.client.loadbalance.RoundRobinConnectionLoadBalancingPolicy"/>
</bean>
<!--org.messaginghub.pooled.jms.JmsPoolConnectionFactory-->
<!--org.apache.activemq.jms.pool.PooledConnectionFactory-->
<bean id="jmsPooledConnectionFactory"
class="org.apache.activemq.jms.pool.PooledConnectionFactory"
init-method="start" destroy-method="stop">
<property name="maxConnections" value="64" />
<property name="MaximumActiveSessionPerConnection"
value="500" />
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory"
ref="jmsPooledConnectionFactory" />
<property name="concurrentConsumers" value="1" />
<property name="artemisStreamingEnabled" value="true"/>
</bean>
<bean id="jms"
class="org.apache.camel.component.jms.JmsComponent">
<property name="configuration" ref="jmsConfig"/>
</bean>
<!-- <bean id="activemq"
class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="jmsConfig" />
</bean>-->
</beans>
Spring-boot Auto (Black)Magic Configuration
I then used the application.yaml file to configure the artemis connection by using this method as outlined in the Spring-boot documentation. This also worked when my application.yaml file contained the following configuration:
artemis:
user: artemis
host: localhost
password: artemis
pool:
max-sessions-per-connection: 500
enabled: true
max-connections: 16
This worked like a charm.
Brave Attempt At Java Configuration.
So I then went for gold and tried the Java based configuration as outlined below:
@SpringBootApplication
@ImportResource("classpath:/camel/camel.xml")
public class ClusterProducerApplication {
public static void main(String[] args) {
SpringApplication.run(ClusterProducerApplication.class, args);
}
@Bean
public JmsComponent jms() throws JMSException {
// Create the connectionfactory which will be used to connect to Artemis
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory();
cf.setBrokerURL("tcp://localhost:61616");
cf.setUser("artemis");
cf.setPassword("artemis");
//Create connection pool using connection factory
PooledConnectionFactory pooledConnectionFactory = new PooledConnectionFactory();
pooledConnectionFactory.setMaxConnections(2);
pooledConnectionFactory.setConnectionFactory(cf);
//Create configuration which uses connection factory
JmsConfiguration jmsConfiguration = new JmsConfiguration();
jmsConfiguration.setConcurrentConsumers(2);
jmsConfiguration.setArtemisStreamingEnabled(true);
jmsConfiguration.setConnectionFactory(pooledConnectionFactory);
// Create the Camel JMS component and wire it to our Artemis configuration
JmsComponent jms = new JmsComponent();
jms.setConfiguration(jmsConfiguration);
return jms;
}
}
So when camel starts up I see the following warning logged on start up:
020-07-28 12:33:38.631 WARN 25329 --- [)-192.168.1.158] o.s.boot.actuate.jms.JmsHealthIndicator : JMS health check failed
javax.jms.JMSSecurityException: AMQ229031: Unable to validate user from /127.0.0.1:42028. Username: null; SSL certificate subject DN: unavailable
After the 5sec delay the timer kicks in and message are being produced. I logged into the Artemis console and I can browse the messages and can see them being created. However when I run a get on actuator health I see the following:
"jms": {
"status": "DOWN",
"details": {
"error": "javax.jms.JMSSecurityException: AMQ229031: Unable to validate user from /127.0.0.1:42816. Username: null; SSL certificate subject DN: unavailable"
}
},
This feels like a big of a bug to me.
Observations about connection pooling implementations.
I noticed that AMQ connection pooling has been moved into the following maven dependency:
<dependency>
<groupId>org.messaginghub</groupId>
<artifactId>pooled-jms</artifactId>
</dependency>
I thought let me give that a try as well. It show the same behaviour as outlined above with one more interesting thing. When using org.messaginghub.pooled-jms
as the connection pool(recommended by spring-boot docs as well) the following is logged on startup.
2020-07-28 12:41:37.255 INFO 26668 --- [ main] o.m.pooled.jms.JmsPoolConnectionFactory : JMS ConnectionFactory on classpath is not a JMS 2.0+ version.
Which is weird as according to the official repo the connector is JMS 2.0 compliant.
Quick Summary: It appears that actuator does not pick up the credentials of the connection factory when configuring the JMS component via Java. While a work around exists at the moment by using the spring-boot application.yaml configuration it limits the way you can configure JMS clients on Camel.
JmsPoolConnectionFactory
(i.e. the pool), not from Camel. It looks like theActiveMQConnectionFactory
you're using is from the ActiveMQ 5.x code-base rather than the version from the ActiveMQ Artemis code-base. Artemis supports both, but only the one from Artemis actually supports JMS 2.0. – Justin Bertram