Spring Container hangs if ActiveMQ is not started - java

I am currently using DefaultMessageListenerContainer to create listeners and JmsTemplate to send messages (producer) to queues.
Spring Configuration Snippet:
#Bean
public ActiveMQConnectionFactory connectionFactory() {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(ActiveMQConnection.DEFAULT_BROKER_URL);
factory.setRedeliveryPolicy(desiredRedeliveryPolicy());
return factory;
}
#Bean
public DefaultMessageListenerContainer requestMessageListenerContainer() {
DefaultMessageListenerContainer requestMessageListenerContainer = new DefaultMessageListenerContainer();
requestMessageListenerContainer.setConcurrentConsumers(noOfconcurrentConsumers);
requestMessageListenerContainer.setConnectionFactory(connectionFactory());
requestMessageListenerContainer.setDestinationName(requestQueueName);
requestMessageListenerContainer.setMessageListener(requestMessageListener());
requestMessageListenerContainer.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
requestMessageListenerContainer.setSessionTransacted(false);
return requestMessageListenerContainer;
}
#Bean
public JmsTemplate requestJmsTemplate() {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(connectionFactory());
jmsTemplate.setDefaultDestination(requestMqQueue());
return jmsTemplate;
}
The issue that I am currently having is that my spring container loading process gets stuck if the ActiveMQ is not started before running the application.
I believe that the DefaultMessageListenerContainer and JmsTemplate are trying to create their connections and session to the ActiveMQConnectionFactory.
Outside of spring, I know if the activemq provided isnt running,
activeMQConnection.createSession()
is where the execution would get stuck. In a regular Java code I can timeout some long processing/potentially stuck process. But how can I do something like that in spring container?
I would like to know if there is any better way to declare these beans so that I would know if the activemq is stuck and the container doesn't get stuck?
Thanks in advance for any help.
Update 1:
I updated my Connection URL for Connection Factory and also added an ExceptionListener:
#Bean
public ActiveMQConnectionFactory connectionFactory() {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(String.format("failover://(%s)?startupMaxReconnectAttempts=1&maxReconnectAttempts=2", ActiveMQConnectionFactory.DEFAULT_BROKER_BIND_URL));
factory.setRedeliveryPolicy(desiredRedeliveryPolicy());
factory.setExceptionListener(factoryExceptionListener());
return factory;
}
public FactoryExceptionListener factoryExceptionListener(){
return new FactoryExceptionListener();
}
public class FactoryExceptionListener implements ExceptionListener {
private static XLogger LOG = XLoggerFactory.getXLogger(FactoryExceptionListener.class);
#Override
public void onException(JMSException exception) {
LOG.error("Factory Exception Caught: "+exception.getMessage());
System.exit(1);
}
}
Now a stupid Question.
I can see the error log getting printed, but the application isn't exiting after System.exit(1). Am I doing something wrong here?
This change helped with the blocking call not being blocking anymore. But I am not able to exit and that means application starts execution and throws a bunch of exceptions as activeMQ isnt available.
What I instead want it to (for now) crash the application. How can I do that?
Update 2:
Instead of exiting out of the application (which is still not working - maybe something to do with the listener) I changed the Exception Listener to make a little more sense for my implementation. I am now trying to get the Broker Up if exception listener is triggered.
public void onException(JMSException exception) {
LOG.error("Factory Exception Caught: "+exception.getMessage());
try {
BrokerService brokerService = new BrokerService();
brokerService.addConnector("tcp://localhost:61616");
brokerService.setDataDirectory("C:/temp/data");
brokerService.setEnableStatistics(true);
brokerService.setPersistent(true);
brokerService.start();
} catch (Exception e) {
e.printStackTrace();
}
}
But I am getting following exception:
2014-07-16 10:24:35.009 [ActiveMQ Task-1] ERROR o.a.a.t.failover.FailoverTransport - Failed to connect to [tcp://localhost:61616] after: 1 attempt(s)
2014-07-16 10:24:35.012 [ActiveMQ Connection Executor: unconnected] ERROR c.b.s.o.b.m.FactoryExceptionListener - Factory Exception Caught: Connection refused: connect
Exception in thread "ActiveMQ Connection Executor: unconnected" java.lang.NoSuchMethodError: org.apache.activemq.transport.TransportFactory.bind(Lorg/apache/activemq/broker/BrokerService;Ljava/net/URI;)Lorg/apache/activemq/transport/TransportServer;
at org.apache.activemq.broker.BrokerService.createTransportConnector(BrokerService.java:2249)
at org.apache.activemq.broker.BrokerService.addConnector(BrokerService.java:291)
at org.apache.activemq.broker.BrokerService.addConnector(BrokerService.java:281)
at com.bhn.service.ordermgmt.bulkorder.mq.FactoryExceptionListener.onException(FactoryExceptionListener.java:19)
at org.apache.activemq.ActiveMQConnection$5.run(ActiveMQConnection.java:1998)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
However when I tried to execute the same code from another project. I was successfully able to get the BrokerService up and running.
I am not sure what this error means and how to resolve it?
Update 3:
Not sure what was wrong earlier but the same code is working now. Thanks for your help #Tim

The reason for this is that the default URL that you are specifying is using the Failover transport. By default the transport will try to connect to the broker until you shut down your app. The createSession call is triggering the client to try and send its connection information request to the Broker but that can't happen until the client connects to the Broker.
One solution which was stated in the comments is to disable the auto startup feature so that the session create calls doesn't get executed on startup. You will however still run into the hang if you later if you trigger a session create while the broker is still down. You can configure the failover transport with a set number of connection attempts using the options shown on the failover transport page.

If you just want to stop the process of connecting if first instance failed,
You can set parameter useKeepAlive=false
I tried other parameters Timeout etc. does not seems to be working.
useKeepAlive works for me.

Related

Too many active consumers for ActiveMQ queue

My Spring app consumes ActiveMQ queue. There are two approaches possible. Initial part of ActiveMQ integration is the same for the both approaches:
#Bean
public ConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory();
}
#Bean
public Queue notificationQueue() {
return resolveAvcQueueByJNDIName("java:comp/env/jms/name.not.important.queue");
}
Single thread approach:
#Bean
public IntegrationFlow orderNotify() {
return IntegrationFlows.from(Jms.inboundAdapter(connectionFactory()).destination(notificationQueue()),
c -> c.poller(Pollers.fixedDelay(QUEUE_POLLING_INTERVAL_MS)
.errorHandler(e -> logger.error("Can't handle incoming message", e))))
.handle(...).get();
}
But I want to consume messages using the several worker threads, so I refactored the code from Inbound adapter to Message driven channel adapter:
#Bean
public IntegrationFlow orderNotify() {
return IntegrationFlows.from(Jms.messageDriverChannelAdapter(connectionFactory()).configureListenerContainer(c -> {
final DefaultMessageListenerContainer container = c.get();
container.setMaxConcurrentConsumers(notifyThreadPoolSize);
}).destination(notificationQueue()))
.handle(...).get();
}
The problem is that the app doesn't stop ActiveMQ's consumer when it being redeployed into Tomcat or being restarted for the second approach. It creates new consumer during it's startup. But all new messages are being routed to the old "dead" consumer so they're sat in the "Pending messages" section and never being dequeued.
What can be the problem here?
You have to stop Tomcat fully, I believe. Typically during redeploy for the application, the Spring container should be stopped and clear properly, but looks like that's not your case: there is something missed for the Tomcat redeploy hook. Therefore I suggest to stop it fully.
Another option is to forget external Tomcat and just migrate to Spring Boot with its ability to start embedded servlet container. This way there is not going to be any leaks after rebuilding and restarting application.

Stop consuming RabbitMQ messages in case of a serious global error

I have developed a simple client for receiving messages from a rabbitMQ queue.
#RabbitListener(queues = "#{'${rabbitmq.queues}'.split(',')}")
public void receiveMessage(Message message, #Header(AmqpHeaders.CONSUMER_QUEUE) String queue) {
logger.debug("message payload as string {}", new String(message.getBody()));
I am having a standard SSL handshake error at the moment because I haven't installed my private key.
Since there is an error, the message is not consumed, it stays in the queue.
The problem is that my client continues to try to consume the same message indefinitely. Could you advice me with a solution to this?
What I want is, in case of a serious global error, to stop consuming messages for a "while". Maybe a Thread sleep? Is there a spring solution to this? Am I missing something here?
By default, the listener container will attempt to reconnect every 5 seconds.
You can reconfigure the container(s), by adding a BackOff to the listener container factory; something like...
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setConcurrentConsumers(3);
final ExponentialBackOff backOff = new ExponentialBackOff();
backOff.setInitialInterval(5_000);
backOff.setMultiplier(1.5);
backOff.setMaxInterval(30_000);
backOff.setMaxElapsedTime(120_000);
factory.setRecoveryBackOff(backOff);
return factory;
}

How to pause and start consuming message using #JmsListener

I am using spring boot version 1.3.2. I am using #JmsListener to consume message from activemq for the message that I created/produced using JmsTemplate. Here is the code:
#JmsListener(destination = "myqueue")
public void consumeMsg(Object requestBody)
try {
javaMailSender.send(requestBody);
} catch (MailException ex) {
LOG.error(ex.getLocalizedMessage(), ex);
if(ex.getMessage().contains(SMTP_CONNECTION_FAILURE) && activeMqMsg.getIntProperty("RETRYCOUNT") == 1) {
producer.send("myqueue",requestBody)
}
else {
producer.send("manualqueue",requestBody)
}
}
}
now when there is a connection failure error from smtp, I want to pause the #JmsListener for SOME time and start again to consume the message. I have not seen a better example for this use case using #JmsListener. Since I am using spring boot, I have added activemq connection parameters in application properties, I do not need to write any code to create connection factory, setting queue...etc can you help out how to do this?
Get a reference to the JmsListenerEndpointRegistry bean (e.g. #Autowire) and call stop() - it will stop all listeners. start() will start all listeners.
If you have multiple listeners and only want to stop 1, give it an id attribute and use registry.getListenerContainer(id), then stop/start the container itself.

Getting org.springframework.transaction.CannotCreateTransactionException when sending a request after long time

I am developing a REST API application using Spring-Boot. It turns that when I start the server (using the embedded tomcat) and I start sending requests to my API, I get the expected responses. But, lets say I wait for 30 minutes before send another request, at that time I get an org.springframework.transaction.CannotCreateTransactionException with root cause java.net.SocketTimeoutException: Read timed out.
My application connects to a remote MySQL server data base.
My WebApplicationStarter class looks looks is the following:
#Configuration
#EnableAutoConfiguration
#ComponentScan("monitec")
public class WebApplicationStarter extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(WebApplicationStarter.class);
}
public static void main(String[] args) throws Exception {
ApplicationContext context = SpringApplication.run(WebApplicationStarter.class, args);
}
#Bean
public SessionFactory sessionFactory(HibernateEntityManagerFactory hemf) {
return hemf.getSessionFactory();
}
#Bean
public EmbeddedServletContainerFactory servletContainerFactory() {
TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
factory.addConnectorCustomizers(connector ->
((AbstractProtocol) connector.getProtocolHandler()).setConnectionTimeout(10000));
factory.setPort(7543);//TODO: Replace this hardcoded value by a system preference
factory.setSessionTimeout(20000);
// configure some more properties
return factory;
}
}
My application.properties is the following:
# Thymeleaf
spring.thymeleaf.cache: false
# Data Source
spring.datasource.url=jdbc:mysql://hostname:8888/schema_name
spring.datasource.username=xxxxxxxxx
spring.datasource.password=xxxxxxxxxxx
# Hibernate
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate4.SpringSessionContext
logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE
#spring.jpa.generate-ddl=true
#spring.jpa.hibernate.ddl-auto=create
I have research several posts and haven't been able to solve my problem. I also set the sessionTimeout to "-1" to make it infinite but it didn't work. I don't know if perhaps it is the MySQL server the one that is closing the connection, and if that's the case I would like to know how to make my application open a new one when a new http request arrive to the server. For now I have not enable any kind of security, I mean I do not require authentication from any client calling my REST API, I'll do it in the future, but for now it is not necessary.
Thank you in advance, I am open to any suggestions and improvements you can give me. If you need my REST Controller code, let me know and I'll post it.
PD: I am using POST MAN REST CLIENT to test my application.
EDIT: I always get the read timed out exception and I can't send any more requests to the server unless I restart the server. This means that after the exception, every request that I send from any client, I keep receiving the exception and the only way to get the expected result is by restarting the application (the embedded tomcat)
I have narrowed the issue to be a problem with Spring-Boot autoconfig managing the connection pool. And I confirmed my diagnose after reading this post
https://aodcoding.wordpress.com/2015/05/22/handling-connection-pool-issues-in-spring-boot/
So, I solve the problem by adding connection pool properties, I decided not to used the C3P0 ones described in the article that I mentioned, but instead I used spring boot ones as follows:
spring.datasource.max-active=50
spring.datasource.initial-size=5
spring.datasource.max-idle=10
spring.datasource.min-idle=5
spring.datasource.test-while-idle=true
spring.datasource.test-on-borrow=true
spring.datasource.validation-query=SELECT 1 FROM DUAL
spring.datasource.time-between-eviction-runs-millis=5000
spring.datasource.min-evictable-idle-time-millis=60000
And as far as I can tell, the problem is solved. I have wait for long time and re send requests to my service and I am getting proper responses.
Next step for me is start enabling spring security configuration to secure the REST services.
Hope this help to any one having same issue I had. Because if you see the exception, is not very clear that the problem is due to connection pool, you would try to hit the problem following the wrong direction.

Detecting when an asynchronous JMS MessageConsumer has an Exception?

I'm processing messages using a JMS MessageConsumer with a MessageListener. If something happens that causes the MessageConsumer to stop receiving and processing messages -- for example, if the underlying connection closes -- how can I detect it? There doesn't seem to be any notification mechanism that I can find in the spec.
I think the question is clear as is, but if you'd like me to post code to clarify the question, just ask!
In case it's important, I'm using ActiveMQ 5.8, although obviously I'd like a scheme that's not implementation-specific.
Use ExceptionListener
If the JMS system detects a problem, it calls the listener's onException method:
public class MyConsumer implements ExceptionListener, MessageListener {
private void init(){
Connection connection = ... //create connection
connection.setExceptionListener(this);
connection.start();
}
public void onException(JMSException e){
String errorCode = e.getErrorCode();
Exception ex = e.getLinkedException();
//clean up resources, or, attempt to reconnect
}
public void onMessage(Message m){
...
}
Not much to it, really, the above is standard practice for standalone consumers; it's not implementation-specific; actually, quite the contrary as it's part of the spec!, so all JMS-compliant providers will support it.

Categories

Resources