Spring Cloud with Liberty - spring-boot

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.

Related

Spring Boot Artemis Server connect Web Console

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.

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.

Spring Boot - Load a Component on Management Context

In order to control the amount of threads on the jetty main embedded server I load a EmbeddedServletContainerCustomizer using the #Component annotation. Im using a different port for the management context and so it seems that a different jetty instance is executed for that port. How can I do the same process for that port or Jetty instance?
Regards
Bruno
Just found out how to solve my issue.
Using the application properties
server.jetty.acceptors
server.jetty.selectors
I can control the amount of threads on both ports. It is not very customisable but it gets the job done. For the main service port, configuring with a EmbeddedServletContainerCustomizer will override these configurations.
Regards

Spring server start-up event

i am facing a problem. I am using tomcat server for my spring maven project and i want to subscribe my application to facebook once when server starts up. I have tried ApplicationListener ContextRefreshedEvent. It gets invoked on application context initialization, but the issue is at that time my server has not completely started and hence my application is not publically accessible which is required in my case as facebook subscription requires verification using application public URL. So i want to do subscription when my server gets started completey and not on application context initialization. Any idea how can i do it? Do i have nay application level event listener that can tell me that server has started completely?
Regards jia
You can annotate a bean method like so :
#PostConstruct
public void yrSubscribeLogic() {}
the app context can only be loaded after the server has fully started.

Spring JMS injection causing application not to startup

We have a spring application that publishes and listens to queues on a remote application server. My publisher and listener which are spring based listen within our own application server.
One of the problems we have for our test environments is the other app. server is not up so when our test application goes to start and it tries to inject JmsTemplate with its connectionFactory it blows up because it is not a valid connection and our entire application fails to load. This is causing grief with the other developers in our group that have nothing to do with JMS. All they want to do is run and test their code but the JmsTemplate connectionFactory is down.
Does anyone have any suggestion for enabling spring ignore some bad injections which will allow our application to start properly?
Thanks
I believe this could be achieved by defining separate spring profiles and then passing it as a parameter in your test environments while starting your application. You could mock or ignore any beans then.
Example
import org.springframework.context.annotation.Profile;
#Profile("test")
public class AppConfigTest {
....
....
}
JVM param / System property
-Dspring.profiles.active=test

Resources