Spring Boot Artemis Server connect Web Console - spring-boot

How do I connect the Web Console to an Spring Boot embedded Artemis Server ?
I have mostly followed this Answer.
tomcat 9.0.58
activemq-web-console-5.16.3.war in webapps folder
added jakarta.servlet.jsp.jstl-1.2.6.jar & jakarta.servlet.jsp.jstl-api-1.2.7.jar into webapps/activemq-web-console-5.16.3/WEB-INF/lib, otherwise the console would not start
added the line set "JAVA_OPTS=%JAVA_OPTS% -Dwebconsole.type=properties -Dwebconsole.jms.url=tcp://localhost:61616 -Dwebconsole.jmx.url=service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi in catalina.bat
But now when I access the webconsole - ERROR:
I get Exception occurred while processing this request, check the log for more information!
and the logs say: IllegalStateException: No broker is found at any of the 1 configured urls
My Artemis Server is running in a minimalistic Spring Boot App:
started with VMOptions: -Dcom.sun.management.jmxremote=true -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.rmi.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
spring boot parent: spring-boot-starter-parent:2.6.2
active mq: artemis-jms-server:2.19.0
spring-boot-starter-web:2.6.2
and the following configuration class:
#Configuration
public class ArtemisConfig implements ArtemisConfigurationCustomizer {
#Autowired
private ArtemisProperties artemisProperties;
#Override
public void customize(org.apache.activemq.artemis.core.config.Configuration configuration) {
try{
configuration.setJMXManagementEnabled(true);
configuration.setSecurityEnabled(false);
configuration.addAcceptorConfiguration("netty", "tcp://localhost:" + artemisProperties.getPort());
} catch (Exception e) {
throw new RuntimeException("queue did not start");
}
}
}

The only real problem the answer you cited is that it was written for ActiveMQ "Classic" rather than ActiveMQ Artemis. ActiveMQ Artemis doesn't use activemq-web-console-5.16.3.war. It uses a web console based on Hawtio 2 which is split up across 3 different war files:
artemis-console.war
artemis-plugin.war
activemq-branding.war
Deploy these to your embedded servlet container (e.g. Tomcat, Jetty, etc.). I don't think you'll need to set any system properties, but during the release process we actually strip out any SLF4J and Log4j jar files so you may need to add those back in if your environment doesn't already provide them. We remove those jars because we actually ship SLF4J in the main lib directory of the standalone broker, and we don't actually need Log4j (since ActiveMQ Artemis uses JBoss Logging) so it's safer to remove it (especially in the wake of all the recent Log4j CVEs). See ARTEMIS-3612 for more details on that.
The web console application running in the browser communicates with the broker via Jolokia which is an HTTP-JMX bridge. Jolokia is part of the Hawtio 2 infrastructure and is included in the aforementioned war files. If the war files are being hosted in the same JVM as your Spring Boot application with ActiveMQ Artemis embedded then you should just need to point the web console app to the same server & port you're using for the console itself. If the web console is hosted separately from the Spring Boot app then you should install Jolokia and then point the web console to it.

Related

How to pass Camel VM messages between multiple Spring Boot applications in Tomcat

We have an application stack, deployed in Tomcat, that consists of several Spring Boot applications. As part of our operations, we want to send some messages to a vm endpoint, where a camel route will consume those messages and then publish them to a JMS topic for any of the other Spring Boot applications that are interested in messages on that topic.
When I start the application stack, there are three spring boot apps that utilize camel, and I see camel start properly in the logs. But when one of the apps sends a message to the vm endpoint, the route that consumes from that endpoint and routes the messages to the jms topic does not seem to get that message. I have placed the camel-core jar in my tomcat lib directory. In the spring boot maven plugin configuration, I have specified an exclusion of the camel-core jar. Oddly enough, that jar is in the WEB-INF/lib of the war anyway! So I have stopped Tomcat, removed that jar from the exploded war, and restarted Tomcat, but that does not change the behavior of the messaging.
Here are the versions that we are using:
Spring Boot 2.3.1
Camel 3.4.2
Tomcat 8.5.5
The first spring boot app that links everything together, with the camel route that consumes from the vm endpoint and produces that message on the jms topic is our "routing engine". It uses camel-spring-boot-starter, spring-boot-starter-artemis, camel-vm-starter, artemis-jms-server and camel-jms-starter. Its RouteBuilder's configure method looks like this:
from("vm:task")
.log(LoggingLevel.WARN, "********** Received task message");
.to("jms:topic:local.private.task")
.routeId("taskToJms");
The app that produces messages to the vm endpoint uses camel-spring-boot-starter and camel-vm-starter. In that app, it has a #Service class that receives a ProducerTemplate that is auto-wired in the constructor. When the application invokes this component to send the message, I see a line in the logs that says
o.a.c.impl.engine.DefaultProducerCache (169) - >>>> vm://task Exchange[]
so it appears that the message is being produced and sent properly to the vm endpoint. However, I see no indication that it has been received/consumed in the routing engine's camel route, since the route's log line is not logging anything, and since I see no other indications of receiving the message in the log. The strange thing is that I am not getting the error of not having any consumers on the vm:task endpoint that I was getting before I put the camel-core jar in tomcat's lib directory.
Am I doing anything obviously wrong? How can I get the spring boot maven plugin to really exclude camel-core? And why are the messages (sent to the vm endpoint) not being consumed by the route in the routing engine? Thanks in advance for any help.
Edit: I was able to keep camel-core out of the war files by adding an exclusion to the configuration of the war plugin, but I was still not able to consume the message on the vm endpoint.
I will post the answer, or at least "an" answer, for anyone who might have found themselves in the puzzling situation that I found myself in.
In short, the answer is that it is best to avoid trying to send VM messages across separate contexts within one big JVM like Tomcat. Instead, use something like JMS. I used Artemis, and I stood up an embedded broker in one of the spring boot apps in tomcat. In other apps (that will be clients), I needed to connect to the embedded artemis server, which requires that you add a #Configuration class (in the module that stands up the embedded broker) that implements ArtemisConfigurationCustomizer:
#Configuration
public class ArtemisConfig implements ArtemisConfigurationCustomizer {
#Override
public void customize(final org.apache.activemq.artemis.core.config.Configuration configuration) {
configuration.addConnectorConfiguration("nettyConnector", new TransportConnfiguration(NettyConnectorFactory.class.getName()));
configuration.addAcceptorConfiguration(new TransportConfiguration(NettyAcceptorFactory.class.getName()));
}
}
That lets your other stuff connect to the embedded Artemis broker. Also, you do not have to worry about upgrading camel-core jars in your tomcat shared lib folder when you upgrade camel to a different version. It's good to keep things simple for maintenance purposes!
Anyway, I hope this helps somebody else who might find themselves here someday.

Using Citrus to mock SFTP and Kafka for Integration Testing Spring-Boot apache-camel xml based routes?

I am working with a Spring Boot application that was written using Apache Camel spring-xml routes. There is very little java based application logic, and it is nearly entirely written in xml and based on the various camel routes.
The routes are configured to connect to the different environments and systems through property files, using a property such as KAFKA_URL and KAFKA_PORT. Inside one of the implemented routes, the application connects with the following and consumes/produces messages to it:
<to id="route_id_replaced_for_question" uri="kafka:{{env:KAFKA_URL:{{KAFKA_URL}}}}:{{env:KAFKA_PORT:{{KAFKA_PORT}}}}?topic={{env:KAFKA_TOPIC:{{topic_to_connect_to}}}}&kerberosRenewJitter=1&kerberosRenewWindowFactor=1&{{kafka.ssl.props}}&{{kafka.encryption.props}}"/>
Additionally, we connect to an SFTP server, which I am also trying to mock using Citrus. That follows a similar pattern where:
<from id="_requestFile" uri="{{env:FTP_URL:{{FTP_URL}}}}:{{env:FTP_PORT:{{FTP_PORT}}}}/{{env:FTP_FILE_DIR:{{FTP_FILE_DIR}}}}/?delete=true&fileExist=Append&password={{env:FTP_PASSWORD:{{FTP_PASSWORD}}}}&delay={{env:FTP_POLL_DELAY:{{FTP_POLL_DELAY}}}}&username={{env:FTP_USER:{{FTP_USER}}}}"/>
Inside of my integration test, I have configured a Citrus' EmbeddedKafkaServer class with the following:
#Bean
public EmbeddedKafkaServer embeddedKafkaServer() {
return new EmbeddedKafkaServerBuilder()
.kafkaServerPort(9092)
.topics("topic_to_connect_to")
.build();
}
and a Citrus FTP server with:
#Bean
public SftpServer sftpServer() {
return CitrusEndpoints.sftp()
.server()
.port(2222)
.autoStart(true)
.user("username")
.password("passwordtoconnectwith")
.userHomePath("filedirectory/filestoreadfrom")
.build();
}
Ideally, my test will connect to the mock sftp server, and I will push a file to the appropriate directory using Citrus, which my application will then read in, process, and publish to a topic on the embedded kafka cluster and verify in the test.
I was under the impression that I would set KAFKA_PORT to 9092 and KAFKA_URL to localhost, as well as FTP_URL to localhost and FTP_PORT to 2222 (amongst the other properties needed) inside of my properties file, but that does not seem to connect me to the embedded cluster or sftp servers..
What piece of the puzzle am I missing to have my spring boot application connect to both of these mocked instances and run its' business logic processing from there?
I resolved this issue - it was due to using a very old version of Kafka (1.0.0 or older), which was missing some of the methods that are called when Citrus attempts to build new topics. If someone encounters a similar problem to this using Citrus, I recommend starting with evaluating the version of Kafka your service is on and determining if it needs to be updated.
For the sftp connection, the server or client was not being autowired, and therefore never starting.

Spring Boot - start ActiveMQ Web Console on startup

I have a Spring Boot app that automatically starts up an ActiveMQ broker (vm://localhost): it works, I can successfully send and receive messages.
I would like Spring Boot to also start the ActiveMQ Web Console e.g. http://localhost:8161/admin (much like it can with the H2 Database console).
Question: how do I make a Spring Boot app start the ActiveMQ Web Console?
Bonus Points: for a specific Spring #profile only?
thanks in advance
Note: I have already reviewed How to enable web console on ActiveMq embedded broker but this requires the use the hawtio which I do not want to/cannot use.
The Web Console is a web app that can be downloaded and started in any servlet container such as Tomcat.
Here are some steps.
Enable ActiveMQ for JMX use in activemq.xml. That is - enable it in the broker tag: <broker useJmx="true" .. and
And make sure createConnector is true.
<managementContext>
<managementContext createConnector="true"/>
</managementContext>
Download the .war from Maven. Better use the same version as the broker.
http://repo1.maven.org/maven2/org/apache/activemq/activemq-web-console/5.14.5/
Then setup the following JVM properties (JAVA_OPTS). Note that URL and ports may differ if you have changed them.
-Dwebconsole.type=properties
-Dwebconsole.jms.url=tcp://localhost:61616
-Dwebconsole.jmx.url=service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi
If you have no Tomcat (or servlet container) and prefer to run your Spring boot apps with "java -jar .. " - you can do the same with the Web console.
Example below using this app: https://github.com/jsimone/webapp-runner
Had to add jstl jar as it wasn't bundled with webapp-runner.
java -Dwebconsole.type=properties -Dwebconsole.jms.url=tcp://localhost:61616 -Dwebconsole.jmx.url=service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi -cp jstl-1.2.jar:webapp-runner.jar webapp.runner.launch --port 8085 activemq-web-console-5.14.5.war
The admin console will be hosted on localhost at port 8085. This is just a starter. You may want to add fail-over, security etc etc. YMMV

Spring Cloud with Liberty

I am currently running a Spring Boot application inside of Websphere Liberty, and use Consul for Service Discovery. To register services with Consul, I created a Liberty feature that hooks in to the Application Lifecycle events and performs the registration /deregistration. This works great, but by doing so I am coupling myself to Liberty. Spring-Cloud-Consul looks like it might solve that issue, but I can't get it to register a service with Liberty (it does connect to Consul) - only with an Embedded Tomcat Server. After looking at the Spring-Cloud-Consul code, the issue is that a EmbeddedServletContainerInitializedEvent isn't being fired, so no port is being set.
My question is, does Spring Cloud Consul only work with embedded servlet containers?
It is a known issue. Feel free to submit a PR. https://github.com/spring-cloud/spring-cloud-consul/issues/173
My solution was to bring spring-cloud-consul's ConsulLifecycle class local and add an ApplicationReadyEvent, like so :
#Autowired(required = false)
private ServerProperties serverProperties;
#EventListener
public void onApplicationReady(ApplicationReadyEvent event) {
this.setConfiguredPort(serverProperties.getPort());
log.info("Application ready on port " + serverProperties.getPort());
this.start();
}
Now my services register and deregister as expected.

configure jndi.xml in serviceMix to work with MQseries

My j2EE app is currently running on ServiceMix. Now i want to add JMS to my app. The application should able to send/receive the JMS message to/from the queue that stays on MQSeries.
mq.hostname=10.3.6.19
mq.channel=CHANNEL
mq.queueManager=QManager
mq.port=1422
What i would like to do is:
1. Create a jndi.xml file and do configuration for jms stuff.
2. my app will initialize the context, look up jndi name, and create a connection, queueManager, queue. .etc
3. Develop send and receive methods.
My question is:
Can you tell me how to do 1st and 2nd steps.
(the script inside ServiceMix's jndi is diffrent with tomcat's
jndi and others.
ServiceMix using Spring based JNDI provider.
http://servicemix.apache.org/jndi-configuration.html)
I just ran into something similar with Weblogic. The following link uses spring-dm to integrate with websphere. It also takes it to the next logical step and adds camel to the mix.
http://lowry-techie.blogspot.com/2010/11/camel-integration-with-websphere-mq.html
Without using Spring-dm, you may run into classloader issues when trying to load the InitialContextFactory from the websphere jar (this is an issue I had with the Weblogic jar)

Resources