Below ActiveMQ implementation is present in code. Sometimes, system stops working and become very slow. When I checked thread dump using JavaMelody - I have seen too many threads are on Runnable state for long time and is not being terminated.
ActiveMQ version - activemq-all-5.3.0.jar
Please refer below code :
Broker :
public class ActiveMQ extends HttpServlet {
private static final long serialVersionUID = -1234568008764323456;
private static final Logger logger = Logger.getLogger(ActiveMQ.class.getName());
public Listener listener;
private String msgBrokerUrl = "tcp://localhost:61602";
public BrokerService broker = null;
public TransportConnector connector = null;
#Override
public void init() throws ServletException {
try {
broker = new BrokerService();
broker.setPersistent(false);
broker.setUseJmx(false);
connector = broker.addConnector(msgBrokerUrl);
broker.setUseShutdownHook(true);
System.out.println("BROKER LOADED");
broker.start();
broker.deleteAllMessages();
listener = new Listener();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Listener:
public class Listener implements MessageListener {
private String msgQueueName = "jms/queue/MessageQueue";
public Session session;
public Destination adminQueue;
public static String id;
public ActiveMQConnection connection;
public MessageConsumer consumer = null;
public Listener() {
try {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(
new URI("failover://(" + "tcp://localhost:61602" + "?wireFormat.cacheEnabled=false"
+ "&wireFormat.maxInactivityDuration=0&wireFormat.tightEncodingEnabled=true)?maxReconnectDelay=1000"));
connection = (ActiveMQConnection) connectionFactory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
adminQueue = session.createQueue(msgQueueName);
id = new Timestamp(new Date().getTime()).toString();
consumer = this.session.createConsumer(this.adminQueue, "ID='" + id + "'");
consumer.setMessageListener(this);
} catch (JMSException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
#SuppressWarnings("unchecked")
#Override
public void onMessage(Message message) {
TextMessage msg = (TextMessage) message;
try {
String xmlMsg = msg.getText();
// business logic
} catch (JMSException ex) {
ex.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
Producer :
public class Producer {
private static String url = "tcp://localhost:61602";
private static String msgQueueName = "jms/queue/MessageQueue";
public ConnectionFactory connectionFactory = null;
public Connection connection = null;
public Session session = null;
public Destination destination = null;
public Producer() {
connectionFactory = new ActiveMQConnectionFactory(url);
}
public void sendResponse(String xml, DataBean objDataBean) {
sendToQueue(xml, msgQueueName, objDataBean);
}
private void sendToQueue(String xml, String msgQueueName, DataBean obj) {
try {
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue(msgQueueName);
MessageProducer producer = session.createProducer(destination);
TextMessage message = session.createTextMessage(xml);
message.setJMSExpiration(1000);
message.setStringProperty(obj.getMsgKey(), obj.getMsgValue());
producer.send(message);
xml = null;
session.close();
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
for (int msg = 0; msg < 20; msg++) {
DataBean obj = getData();
new Producer().sendResponse(xml, obj);
;
}
}
}
Hanging Threads Exception details :
Type 1 :
ActiveMQ Transport: tcp:///127.0.0.1:41818
java.net.SocketInputStream.socketRead0(Native Method)
java.net.SocketInputStream.read(SocketInputStream.java:152)
java.net.SocketInputStream.read(SocketInputStream.java:122)
org.apache.activemq.transport.tcp.TcpBufferedInputStream.fill(TcpBufferedInputStream.java:50)
org.apache.activemq.transport.tcp.TcpBufferedInputStream.read(TcpBufferedInputStream.java:58)
java.io.DataInputStream.readInt(DataInputStream.java:387)
org.apache.activemq.openwire.OpenWireFormat.unmarshal(OpenWireFormat.java:272)
org.apache.activemq.transport.tcp.TcpTransport.readCommand(TcpTransport.java:210)
org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:202)
org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:185)
java.lang.Thread.run(Thread.java:745)
Type 2 :
ActiveMQ Transport: tcp://localhost/127.0.0.1:61602
java.net.SocketInputStream.socketRead0(Native Method)
java.net.SocketInputStream.read(SocketInputStream.java:152)
java.net.SocketInputStream.read(SocketInputStream.java:122)
org.apache.activemq.transport.tcp.TcpBufferedInputStream.fill(TcpBufferedInputStream.java:50)
org.apache.activemq.transport.tcp.TcpBufferedInputStream.read(TcpBufferedInputStream.java:58)
java.io.DataInputStream.readInt(DataInputStream.java:387)
org.apache.activemq.openwire.OpenWireFormat.unmarshal(OpenWireFormat.java:272)
org.apache.activemq.transport.tcp.TcpTransport.readCommand(TcpTransport.java:210)
org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:202)
org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:185)
java.lang.Thread.run(Thread.java:745)
Please could you give some hints on this issue for further investigation.
Edit:
I read few posts on internet and concluded that I must update activemq jar file and implement timeout but when I started reading about timeout setting then I got confused whether I should set timeout in producer and consumer or failover or on message or broker service. Timeout at each component has different purpose then where I should implement timeout considering above code and exception.
Creating a connection is very expensive and when you close it, the port is retained for up to 3 minutes to ensure it is shutdown cleanly.
You want to create connections only when you really have to avoid performance problems. I suggest you create the connection once, and keep that connection open unless you get an error. This can improve performance by 2 to 3 orders of magnitude.
This is a good performance tuning pattern which applies in many cases;
only create and destroy expensive resources when you really have to.
operations you perform many times should be kept to a minimum. i.e do repeatedly as little as possible.
Related
I have configured JMS topic in JBOSS_EAP_7.0 and write a simple java code to create a message producer. I have the following stateless bean
#Stateless
public class ExchangeSenderFacadeWrapperBean {
private static final OMSLogHandlerI logger = new Log4j2Handler("ClientSenderFacadeBean");
#Resource(lookup = "java:/JmsXA") // inject ConnectionFactory (more)
protected ConnectionFactory factory;
#Resource(lookup = "java:/jms/topic/ORD_CLINT_PUSH")
protected Topic target;
private Connection connection = null;
private Session session = null;
public void sendMessage(String message) {
MessageProducer producer= null;
try {
if(connection==null){ //todo verify
connection = factory.createConnection();
}
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(target);
producer.setDisableMessageID(true);
TextMessage outmsg = session.createTextMessage(message);
producer.send(outmsg);
logger.info("Message was sent to Topic");
producer.setTimeToLive(900000);//15min //todo
} catch (Exception e) {
logger.error(" Error when sending order to jboss:", e);
throw new OMSCoreRuntimeException(e.getMessage(), e);
} finally {
try {
if (producer != null)
producer.close();
} catch (JMSException e) {
logger.warn("\n jms producer close error:",e);
}
try {
if (session != null)
session.close();
} catch (JMSException e) {
logger.warn("\n jms session close error:",e);
}
}
}
This works fine until i made simple change to move sendMessage(String message) method to pojo class as follow.
#Stateless(name = "ExchangeSenderFacadeBean")
#Local({ExchangeSenderFacadeLocalI.class})
public class ExchangeSenderFacadeWrapperBean implements ExchangeSenderFacadeLocalI {
#Resource(lookup = "java:/JmsXA") // inject ConnectionFactory (more)
protected ConnectionFactory factory;
#EJB(beanName = "BeanRegistryLoader")
protected BeanRegistryLoader omsRegistryBean;
protected BeanRegistryCore beanRegistryCore;
#Resource(lookup = "java:/jms/queue/ToExchange")
protected Queue target;
private ExchangeSenderFacadeCoreI exchangeSenderFacadeCore;
#Override
public void sendToExchange(ExchangeMessage exchangeMessage) {
exchangeSenderFacadeCore.sendToExchange(exchangeMessage);
}
#PostConstruct
public void init() {
beanRegistryCore = omsRegistryBean.registry();
if (exchangeSenderFacadeCore == null) {
exchangeSenderFacadeCore = ((BeanRegistryCore) omsRegistryBean.registry()).getExchangeSenderFacadeCoreI();
exchangeSenderFacadeCore.setBeanRegistryCore(omsRegistryBean.registry());
exchangeSenderFacadeCore.setFactory(factory);
exchangeSenderFacadeCore.setTargetQueue(target);
}
}
}
ConnectionFactory and target Queue variables set inside EJB PostConstruct method and pojo class looks like follow, which now contains logic to create and publish method to EJB queue
public class ExchangeSenderFacadeCore implements ExchangeSenderFacadeCoreI {
private static final OMSLogHandlerI logger = new Log4j2HndlAdaptor("ExchangeSenderFacadeCore");
private BeanRegistryCore beanRegistryCore;
private ConnectionFactory factory;
private Connection connection = null;
private Session session = null;
private long ttl = 900000;
protected Queue targetQueue;
public ExchangeSenderFacadeCore() {
if (System.getProperty(OMSConst.SYS_PROPERTY_JMS_TTL) != null && System.getProperty(OMSConst.SYS_PROPERTY_JMS_TTL).length() > 0) {
ttl = Long.parseLong(System.getProperty(OMSConst.SYS_PROPERTY_JMS_TTL));
}
logger.info("LN:103", "==JMS Topic TTL:" + ttl);
}
#Override
public void processSendToExchange(ExchangeMessage exchangeMessage) {
sendToExchange(exchangeMessage);
}
public boolean isParallelRunEnabled() {
Object isParallelRun = beanRegistryCore.getCacheAdaptorI().cacheGet(OMSConst.DEFAULT_TENANCY_CODE, OMSConst.APP_PARAM_IS_PARALLEL_RUN, CACHE_NAMES.SYS_PARAMS_CACHE_CORE);
if (isParallelRun != null && String.valueOf(isParallelRun).equals(OMSConst.STRING_1)) {
return true;
}
return false;
}
#Override
public void sendToExchange(ExchangeMessage exchangeMessage) {
MessageProducer producer = null;
try {
if (isParallelRunEnabled()) {
logger.info("LN:66", "== Message send to exchange skipped,due to parallel run enabled");
return;
}
if (connection == null) {
connection = factory.createConnection();
}
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(targetQueue);
producer.setDisableMessageID(true);
Message message = beanRegistryCore.getJmsExchangeMsgTransformerI().transformToJMSMessage(session, exchangeMessage);
producer.send(message);
producer.setTimeToLive(ttl);//default 15min
logger.elkLog("78", "-1", LogEventsEnum.SENT_TO_EXCHANGE, exchangeMessage.toString());
} catch (Exception e) {
logger.error("LN:80", " Error when sending order to exchange:", e);
throw new OMSCoreRuntimeException(e.getMessage(), e);
} finally {
try {
if (producer != null)
producer.close();
} catch (JMSException e) {
logger.error("LN:87", "JMS producer close error:", e);
}
try {
if (session != null)
session.close();
} catch (JMSException e) {
logger.error("LN:93", "JMS session close error:", e);
}
}
}
#Override
public void processSendToExchangeSync(ExchangeMessage exchangeMessage) {
}
#Override
public BeanRegistryCore getBeanRegistryCore() {
return beanRegistryCore;
}
#Override
public void setBeanRegistryCore(BeanRegistryCore beanRegistryCore) {
this.beanRegistryCore = beanRegistryCore;
}
#Override
public ConnectionFactory getFactory() {
return factory;
}
#Override
public void setFactory(ConnectionFactory factory) {
this.factory = factory;
}
#Override
public Queue getTargetQueue() {
return targetQueue;
}
#Override
public void setTargetQueue(Queue targetQueue) {
this.targetQueue = targetQueue;
}
}
But when i execute moderated code it gives me following error
javax.ejb.EJBTransactionRolledbackException: Producer is closed
Any possible fixes?
After a deep search deep into the problem, I found this https://developer.jboss.org/wiki/ShouldICacheJMSConnectionsAndJMSSessions article posted on one of JBOSS developer thread. This explains clearly the reason for caching connection and other JMS related resources being an anti-pattern for JMS code is running in a JEE application server.
In nutshell JCA layer pools JMS connections and JMS sessions. So when you call createConnection() or createSession(), then, in most cases it's not really calling the actual JMS implementation to actually create a new JMS connection or JMS session, it's just returning one from its own internal cache.
Additionally JBOSS server too manages stateless session bean pool. A stateless session bean is available on the connection pool only after you are done with the purpose of it, but not prior. Meantime Connection (Either JMS newly created or Cached) used to create JMS Session (session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE)) inside stateless session bean, also done with its purpose and too available on JCA layer connection pool. Therefore calling cached connection inside stateless EJB class as follow will not give you an exception even though it is not recommended by Oracle.
public void sendToExchange(ExchangeMessage exchangeMessage) {
MessageProducer producer = null;
try {
if (isParallelRunEnabled()) {
logger.info("LN:66", "== Message send to exchange skipped,due to parallel run enabled");
return;
}
if (connection == null) {
connection = factory.createConnection();
}
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(targetQueue);
producer.setDisableMessageID(true);
Message message = beanRegistryCore.getJmsExchangeMsgTransformerI().transformToJMSMessage(session, exchangeMessage);
producer.send(message);
producer.setTimeToLive(ttl);//default 15min
logger.elkLog("78", "-1", LogEventsEnum.SENT_TO_EXCHANGE, exchangeMessage.toString());
} catch (Exception e) {
logger.error("LN:80", " Error when sending order to exchange:", e);
throw new OMSCoreRuntimeException(e.getMessage(), e);
} finally {
try {
if (producer != null)
producer.close();
} catch (JMSException e) {
logger.error("LN:87", "JMS producer close error:", e);
}
try {
if (session != null)
session.close();
} catch (JMSException e) {
logger.error("LN:93", "JMS session close error:", e);
}
}
}
But in this case, since the same POJO class instance can be used on multiple occasions as bellow. It does not guarantee that the connection is freed and available in the JCA layer connection pool and gives exceptions.
#PostConstruct
public void init() {
beanRegistryCore = omsRegistryBean.registry();
if (exchangeSenderFacadeCore == null) {
exchangeSenderFacadeCore = ((BeanRegistryCore) omsRegistryBean.registry()).getExchangeSenderFacadeCoreI();
exchangeSenderFacadeCore.setBeanRegistryCore(omsRegistryBean.registry());
exchangeSenderFacadeCore.setFactory(factory);
exchangeSenderFacadeCore.setTargetQueue(target);
}
}
Using Wildfly and JMS via ActiveMQ I got following exception.
javax.ejb.EJBTransactionRolledbackException: Producer is closed
I have the following stateless bean
#Stateless(name = "ExchangeSenderFacadeBean")
#Local({ExchangeSenderFacadeLocalI.class})
public class ExchangeSenderFacadeWrapperBean implements ExchangeSenderFacadeLocalI {
#Resource(lookup = "java:/JmsXA") // inject ConnectionFactory (more)
protected ConnectionFactory factory;
#EJB(beanName = "BeanRegistryLoader")
protected BeanRegistryLoader omsRegistryBean;
protected BeanRegistryCore beanRegistryCore;
#Resource(lookup = "java:/jms/queue/ToExchange")
protected Queue target;
private ExchangeSenderFacadeCoreI exchangeSenderFacadeCore;
#Override
public void sendToExchange(ExchangeMessage exchangeMessage) {
exchangeSenderFacadeCore.sendToExchange(exchangeMessage);
}
#PostConstruct
public void init() {
beanRegistryCore = omsRegistryBean.registry();
if (exchangeSenderFacadeCore == null) {
exchangeSenderFacadeCore = ((BeanRegistryCore) omsRegistryBean.registry()).getExchangeSenderFacadeCoreI();
exchangeSenderFacadeCore.setBeanRegistryCore(omsRegistryBean.registry());
exchangeSenderFacadeCore.setFactory(factory);
exchangeSenderFacadeCore.setTargetQueue(target);
}
}
}
And I use simple java class to create a method which produces a message and send it to the destination as follow
public class ExchangeSenderFacadeCore implements ExchangeSenderFacadeCoreI {
private static final OMSLogHandlerI logger = new Log4j2HndlAdaptor("ExchangeSenderFacadeCore");
private BeanRegistryCore beanRegistryCore;
private ConnectionFactory factory;
private Connection connection = null;
private Session session = null;
private long ttl = 900000;
protected Queue targetQueue;
public ExchangeSenderFacadeCore() {
if (System.getProperty(OMSConst.SYS_PROPERTY_JMS_TTL) != null && System.getProperty(OMSConst.SYS_PROPERTY_JMS_TTL).length() > 0) {
ttl = Long.parseLong(System.getProperty(OMSConst.SYS_PROPERTY_JMS_TTL));
}
logger.info("LN:103", "==JMS Topic TTL:" + ttl);
}
#Override
public void processSendToExchange(ExchangeMessage exchangeMessage) {
sendToExchange(exchangeMessage);
}
public boolean isParallelRunEnabled() {
Object isParallelRun = beanRegistryCore.getCacheAdaptorI().cacheGet(OMSConst.DEFAULT_TENANCY_CODE, OMSConst.APP_PARAM_IS_PARALLEL_RUN, CACHE_NAMES.SYS_PARAMS_CACHE_CORE);
if (isParallelRun != null && String.valueOf(isParallelRun).equals(OMSConst.STRING_1)) {
return true;
}
return false;
}
#Override
public void sendToExchange(ExchangeMessage exchangeMessage) {
MessageProducer producer = null;
try {
if (isParallelRunEnabled()) {
logger.info("LN:66", "== Message send to exchange skipped,due to parallel run enabled");
return;
}
if (connection == null) {
connection = factory.createConnection();
}
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(targetQueue);
producer.setDisableMessageID(true);
Message message = beanRegistryCore.getJmsExchangeMsgTransformerI().transformToJMSMessage(session, exchangeMessage);
producer.send(message);
producer.setTimeToLive(ttl);//default 15min
logger.elkLog("78", "-1", LogEventsEnum.SENT_TO_EXCHANGE, exchangeMessage.toString());
} catch (Exception e) {
logger.error("LN:80", " Error when sending order to exchange:", e);
throw new OMSCoreRuntimeException(e.getMessage(), e);
} finally {
try {
if (producer != null)
producer.close();
} catch (JMSException e) {
logger.error("LN:87", "JMS producer close error:", e);
}
try {
if (session != null)
session.close();
} catch (JMSException e) {
logger.error("LN:93", "JMS session close error:", e);
}
}
}
#Override
public void processSendToExchangeSync(ExchangeMessage exchangeMessage) {
}
#Override
public BeanRegistryCore getBeanRegistryCore() {
return beanRegistryCore;
}
#Override
public void setBeanRegistryCore(BeanRegistryCore beanRegistryCore) {
this.beanRegistryCore = beanRegistryCore;
}
#Override
public ConnectionFactory getFactory() {
return factory;
}
#Override
public void setFactory(ConnectionFactory factory) {
this.factory = factory;
}
#Override
public Queue getTargetQueue() {
return targetQueue;
}
#Override
public void setTargetQueue(Queue targetQueue) {
this.targetQueue = targetQueue;
}
}
ExchangeSenderFacadeCoreI is interface class but when I execute this code I get above exception but if I move sendToExchange() method in ExchangeSenderFacadeCore to ExchangeSenderFacadeWrapperBean class then the error will disappear. Can anyone tell me the exact reason for this scenario
After a deep search deep into the problem, I found this
https://developer.jboss.org/wiki/ShouldICacheJMSConnectionsAndJMSSessions article posted on one of JBOSS developer thread. This explains clearly the reason for caching connection and other JMS related resources being an anti-pattern for JMS code is running in a JEE application server.
In nutshell JCA layer pools JMS connections and JMS sessions. So when you call createConnection() or createSession(), then, in most cases it's not really calling the actual JMS implementation to actually create a new JMS connection or JMS session, it's just returning one from its own internal cache.
Additionally JBOSS server too manages stateless session bean pool. A stateless session bean is available on the connection pool only after you are done with the purpose of it, but not prior. Meantime Connection (Either JMS newly created or Cached) used to create JMS Session (session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE)) inside stateless session bean, also done with its purpose and too available on JCA layer connection pool. Therefore calling cached connection inside stateless EJB class as follow will not give you an exception even though it is not recommended by Oracle.
public void sendToExchange(ExchangeMessage exchangeMessage) {
MessageProducer producer = null;
try {
if (isParallelRunEnabled()) {
logger.info("LN:66", "== Message send to exchange skipped,due to parallel run enabled");
return;
}
if (connection == null) {
connection = factory.createConnection();
}
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(targetQueue);
producer.setDisableMessageID(true);
Message message = beanRegistryCore.getJmsExchangeMsgTransformerI().transformToJMSMessage(session, exchangeMessage);
producer.send(message);
producer.setTimeToLive(ttl);//default 15min
logger.elkLog("78", "-1", LogEventsEnum.SENT_TO_EXCHANGE, exchangeMessage.toString());
} catch (Exception e) {
logger.error("LN:80", " Error when sending order to exchange:", e);
throw new OMSCoreRuntimeException(e.getMessage(), e);
} finally {
try {
if (producer != null)
producer.close();
} catch (JMSException e) {
logger.error("LN:87", "JMS producer close error:", e);
}
try {
if (session != null)
session.close();
} catch (JMSException e) {
logger.error("LN:93", "JMS session close error:", e);
}
}
}
But in this case, since the same POJO class instance can be used on multiple occasions as bellow. It does not guarantee that connection is freed and available in the JCA layer connection pool and gives exceptions.
#PostConstruct
public void init() {
beanRegistryCore = omsRegistryBean.registry();
if (exchangeSenderFacadeCore == null) {
exchangeSenderFacadeCore = ((BeanRegistryCore) omsRegistryBean.registry()).getExchangeSenderFacadeCoreI();
exchangeSenderFacadeCore.setBeanRegistryCore(omsRegistryBean.registry());
exchangeSenderFacadeCore.setFactory(factory);
exchangeSenderFacadeCore.setTargetQueue(target);
}
}
I have created a simple Console Application listening to messages on Rabbit MQ. Works fine. No problems there. But how do I close the connection. I've been googleing arround a lot, but I didn't find any clear answers. The closest I got to an answer is this SO question: What is the best way to safely end a java application with running RabbitMQ consumers, but the answer omits the most important part: The code!
Here is my code:
package com.company;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;
interface ObjectRecievedListener {
void objectRecieved(Object obj);
}
public class RabbitMQReceiver extends RabbitMQBase
{
ArrayList<DefaultConsumer> consumers = new ArrayList<>();
private List<ObjectRecievedListener> listeners = new ArrayList<>();
private final Connection connection;
private final Channel channel;
public RabbitMQReceiver(RabbitMQProperties properties) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(properties.getHost());
factory.setPassword(properties.getPassword());
factory.setUsername(properties.getLogin());
connection = factory.newConnection();
channel = connection.createChannel();
channel.queueDeclare(properties.getInboundQueueName(), true, false, false, null);
channel.basicQos(1);
final Consumer consumer = new DefaultConsumer(channel) {
#Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
try {
doWork(message);
} finally {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
consumers.add((DefaultConsumer) consumer);
boolean autoAck = false;
channel.basicConsume(properties.getInboundQueueName(), autoAck, consumer);
}
public void addListener(ObjectRecievedListener listener) {
listeners.add(listener);
}
public void stop() {
try {
channel.close();
connection.close();
} catch (Exception e) {
}
}
private void doWork(String message) {
Object obj = getObjectFromXML(message);
for (ObjectRecievedListener l : listeners)
l.objectRecieved(obj);
stop();
}
}
But it doesn't stop just because I called stop()
So in short: How do I close the connection to Rabbit MQ?
The RabbitMQ team monitors the rabbitmq-users mailing list and only sometimes answers questions on StackOverflow.
When you say "it doesn't stop" have you confirmed that the stop() method is actually called? Is your code hanging on one of the close() methods?
The channel instance will have a basicCancel method that you could call to cancel the current consumer before closing the channel and connection. To be honest, closing the channel will cancel your consumer so I doubt this is the root cause of the issue.
Try this:
let connection = null;
connection = await amqp.connect(`amqp://${config.username}:${config.password}#${config.host}:${config.port}`);
connection.close();
We have a java web application that sends (JobsController.java) and receives messages (JMSMessageListener.java) via JMS. After running the application under constant load for 24 hours and taking heap dumps, I observe a constant increase in memory usage that the application does not let go when in idle state. I know that this is going to cause a java heap out of memory issue.
JobsController is an ejb stateless bean and its resources are destroyed correctly after each call.
JMSMessageListener gets handled by ejb global bean pool and its instance is reused.
The suspects i can see from the java heap dump are
EJB bean injection is causing a memory leak
https://blog.akquinet.de/2017/01/04/dont-get-trapped-into-a-memory-leak-using-cdi-instance-injection/
ActiveMQConnection.finalize(). If it is the culprit than it must
happen to all those wildfly activemq deployments. Any hint is
appreciated.
ActiveMQConnection.java
#Override
protected final void finalize() throws Throwable {
if (!closed) {
if (this.factoryReference.isFinalizeChecks()) {
ActiveMQJMSClientLogger.LOGGER.connectionLeftOpen(creationStack);
}
close();
}
JobsController
#Stateless
public class JobsController {
#Inject
private JMSContext jmsContext;
private Connection connection;
private Session session;
private MessageProducer jmsProducer;
#Resource(lookup = "java:/ConnectionFactory")
private ConnectionFactory connectionFactory;
#Resource(lookup = JAVA_JMS_JOB_QUEUE)
private Queue jobQueue;
#Resource(lookup = JAVA_JMS_QUEUE)
private Queue progressQueue;
#PreDestroy
void release() {
try {
if (jmsProducer != null) {
jmsProducer.close();
}
if (session != null) {
session.close();
}
if (jmsContext != null) {
jmsContext.close();
}
if (connection !=null) {
connection.close();
}
} catch (JMSException e) {
LOG.warn("failed to close JMS resources: {}", e.getMessage());
}
}
public synchronized MessageProducer getJmsProducer() {
if (jmsProducer == null) {
try {
connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
jmsProducer = session.createProducer(jobQueue);
connection.start();
} catch (JMSException e) {
LOG.error("failed to setup JMS message producer: {}", e.getMessage());
}
}
return jmsProducer;
}
public void addMessageToProgressQueue(ProgressMessage progressMessage) {
ObjectMessage objectMessage = jmsContext.createObjectMessage(progressMessage);
try {
getJmsProducer().send(progressQueue, objectMessage);
} catch (JMSException e) {
LOG.error("failed to send progress message {}: {}", objectMessage, e.getMessage());
}
}
}
JMSMessageListener.java
#MessageDriven(name = "JMSMessageListener", mappedName = JAVA_JMS_QUEUE, activationConfig = {
#ActivationConfigProperty(
propertyName = "acknowledgeMode",
propertyValue = "Auto-acknowledge"),
#ActivationConfigProperty(
propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
#ActivationConfigProperty(
propertyName = "destination",
propertyValue = JAVA_JMS_QUEUE)
})
public class JMSMessageListener implements MessageListener {
private static Logger LOG = LoggerFactory.getLogger(JMSMessageListener.class);
#EJB
private JobsController jobsController;
private final ObjectMapper progressMessageMapper;
public JMSMessageListener() {
progressMessageMapper = new ObjectMapper();
progressMessageMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
}
#Override
public void onMessage(Message message) {
ProgressMessage progressMessage = null;
try {
if (message instanceof BytesMessage) {
BytesMessage bytesMessage = (BytesMessage) message;
int TEXT_LENGTH = new Long(bytesMessage.getBodyLength()).intValue();
byte[] textBytes = new byte[TEXT_LENGTH];
bytesMessage.readBytes(textBytes, TEXT_LENGTH);
String progressText = new String(textBytes, "UTF-8");
progressText = progressText.replaceAll("'totalSteps': None", "'totalSteps': 0");
progressMessage = progressMessageMapper.readValue(progressText, ProgressMessage.class);
} else if (message instanceof ObjectMessage) {
progressMessage = message.getBody(ProgressMessage.class);
}
if (progressMessage != null) {
jobsController.sendProgressMessage(progressMessage);
} else {
LOG.error("An empty progress message was received");
}
} catch (JMSException | IOException e) {
LOG.error("failed to process progress message: {}", e.getMessage(), e);
}
}
}
Couple of things:
You're injecting a JMSContext but never using it (at least in the code that you pasted). This seems like an error.
If you aren't going to use the injected JMSContext but rather use the injected ConnectionFactory then you should be injecting "java:/JmsXA" rather than "java:/ConnectionFactory" since it is a &tl;pooled-connection-factory>. Creating a connection for every message sent with "java:/ConnectionFactory" is an anti-pattern since it is not pooled. Also, I assume you'd want to use an XA transaction so that message consumption and sending in your MDB is atomic and that won't work with "java:/ConnectionFactory".
I am newbie in JMS and OpenMQ. I am using both in my code with multi threading but I am getting this weird output when executing the code. Can anyone please explain how this statements in multi threading executes?
public void receiveData(){
try {
Hashtable<String, String> contextParams = new Hashtable<>();
contextParams.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.fscontext.RefFSContextFactory");
contextParams.put(Context.PROVIDER_URL, "file:///C:/jndiStorage");
Context ctx = new InitialContext(contextParams);
QueueReceiver server = new QueueReceiver(ctx, "firstQueueConnectionFactory", "firstQueue");
server.start();
System.out.println("staement 1");
}catch (NamingException e) {
System.out.println("JNDI Problem. Check used hostname and/or Glassfish configuration.");
e.printStackTrace();
}
}
And my other method is something like this which return list of objects received
public List<Ticket> getAllTickets() throws TicketException {
receiveData();
System.out.println("statement 2");
return JMSLocalTicketStore.entrySet().stream().map(entry -> (Ticket) entry.getValue().clone()).collect(Collectors.toList());
}
QueueReceiver class which is extending Thread class is something like this :
public QueueReceiver(Context ctx, String connFactoryName, String queueName)
throws NamingException {
// Look up the connection factory object in the JNDI context provided
connFactory = (ConnectionFactory) ctx.lookup(connFactoryName);
// Look up the Destination in the JNDI context
destination = (Destination) ctx.lookup(queueName);
}
private void startServer() {
System.out.println("\t [RECEIVER]: Start waiting for messages");
active = true;
// Create JMS Context
try (JMSContext jmsContext = connFactory.createContext()) {
// Create a JMSConsumer to receive message
JMSConsumer consumer = jmsContext.createConsumer(destination);
while (active) {
Ticket ticket = (Ticket) consumer.receiveBody(Ticket.class, 5000);
// if no message is received with 5 secs messsage == null
if (ticket != null) {
System.out.println("Reveicer statement 1");
stopServer();
}else{
empty();
}
}
}
System.out.println("\t [RECEIVER]: Stopped.");
}
public void empty(){
active = false;
System.out.println("No message in the OpenMQ");
}
public void stopServer() {
active = false;
System.out.println("\t [RECEIVER]: Stopping to listen for messages.");
}
#Override
public void run() {
startServer();
}
}
And my output is :
staement 1
statement 2
[RECEIVER]: Start waiting for messages
Reveicer statement 1
[RECEIVER]: Stopping to listen for messages.
[RECEIVER]: Stopped.
Why it is not printing all the receiver statements before statement 2 of getAllTickets()