How is it possible to enqueue messages to different queues using a persistent connection / session in ActiveMQ?
What I have done:
public class ActiveMQProducer {
private static final Logger LOGGER = Logger.getLogger(ActiveMQProducer.class);
private Connection connection;
private MessageProducer producer;
private Session session;
String activeMQConnection;
public ActiveMQProducer() throws ConfigurationException, JMSException {
activeMQConnection = ActiveMQPropertyManagerFactory.getInstance().getString("active.mq.url");
}
public void setupActiveMQ(String queueName) throws JMSException {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(activeMQConnection);
factory.setRejectedTaskHandler(new ThreadPoolExecutor.CallerRunsPolicy());
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = session.createQueue(queueName);
producer = session.createProducer(queue);
}
public void getConnection(String queueName) throws JMSException {
if (connection == null || session == null) {
Object object = new Object();
synchronized (object) {
setupActiveMQ(queueName);
}
}
}
public <T extends Serializable> T sendToActiveMQ(String queueName, T t) throws JMSException {
getConnection(queueName);
ObjectMessage message = session.createObjectMessage(t);
producer.send(message);
return null;
}
public void sendMessageToActiveMQ(String queueName, String message) throws JMSException {
getConnection(queueName);
TextMessage toSend = session.createTextMessage(message);
producer.send(toSend);
}
}
I have realized by using this and sending a message to a different queue eventually ActiveMQ runs out of connections because I never close the connection or session:
org.apache.activemq.transport.tcp.ExceededMaximumConnectionsException: Exceeded the maximum number of allowed client connections.
What would be the proper way to handle this? I have about 5 queues I have to send different messages to, should I open a new connection, enqueue and close the connection, is there anyway to keep the session / connections persistent?
Thanks.
here some solutions :
1 Producer per destination :
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
import javax.jms.Connection;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.ConfigurationException;
import org.apache.log4j.Logger;
public class ActiveMQProducer {
private static final Logger LOGGER = Logger.getLogger(ActiveMQProducer.class);
private Connection connection;
private Session session;
String activeMQConnection;
Map<String, MessageProducer> producers = Collections.synchronizedMap(new HashMap<String, MessageProducer>());
Thread shutdownHook = new Thread(new Runnable() {
#Override
public void run() {
close();
}
});
public ActiveMQProducer() throws ConfigurationException, JMSException {
activeMQConnection = ActiveMQPropertyManagerFactory.getInstance().getString("active.mq.url");
setupActiveMQ();
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
public void setupActiveMQ() throws JMSException {
close();
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(activeMQConnection);
factory.setRejectedTaskHandler(new ThreadPoolExecutor.CallerRunsPolicy());
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
}
#Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
public void close() {
if (connection != null) {
try {
connection.close();
} catch (Exception e) {
}
connection = null;
}
}
public void getConnection() throws JMSException {
if (connection == null || session == null) {
setupActiveMQ();
}
}
public MessageProducer getProducer(String queueName) throws JMSException {
getConnection();
MessageProducer producer = producers.get(queueName);
if (producer == null) {
Queue queue = session.createQueue(queueName);
producer = session.createProducer(queue);
producers.put(queueName, producer);
}
return producer;
}
public <T extends Serializable> T sendToActiveMQ(String queueName, T t) throws JMSException {
MessageProducer producer = getProducer(queueName);
ObjectMessage message = session.createObjectMessage(t);
producer.send(message);
return null;
}
public void sendMessageToActiveMQ(String queueName, String message) throws JMSException {
MessageProducer producer = getProducer(queueName);
TextMessage toSend = session.createTextMessage(message);
producer.send(toSend);
}
}
1 Producer for all destinations :
import java.io.Serializable;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
import javax.jms.Connection;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.ConfigurationException;
import org.apache.log4j.Logger;
public class ActiveMQProducer2 {
private static final Logger LOGGER = Logger.getLogger(ActiveMQProducer2.class);
private Connection connection;
private Session session;
String activeMQConnection;
Map<String, Destination> destinations = Collections.synchronizedMap(new HashMap<String, Destination>());
private MessageProducer producer;
Thread shutdownHook = new Thread(new Runnable() {
#Override
public void run() {
close();
}
});
public ActiveMQProducer2() throws ConfigurationException, JMSException {
activeMQConnection = ActiveMQPropertyManagerFactory.getInstance().getString("active.mq.url");
setupActiveMQ();
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
public void setupActiveMQ() throws JMSException {
close();
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(activeMQConnection);
factory.setRejectedTaskHandler(new ThreadPoolExecutor.CallerRunsPolicy());
connection = factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
producer = session.createProducer(session.createTemporaryQueue());
}
#Override
protected void finalize() throws Throwable {
close();
super.finalize();
}
public void close() {
if (connection != null) {
try {
connection.close();
} catch (Exception e) {
}
connection = null;
}
}
public void getConnection() throws JMSException {
if (connection == null || session == null) {
setupActiveMQ();
}
}
public Destination getDestination(String queueName) throws JMSException {
getConnection();
Destination destination = destinations.get(queueName);
if (destination == null) {
destination = session.createQueue(queueName);
destinations.put(queueName, destination);
}
return destination;
}
public <T extends Serializable> T sendToActiveMQ(String queueName, T t) throws JMSException {
Destination destination = getDestination(queueName);
ObjectMessage message = session.createObjectMessage(t);
producer.send(destination, message);
return null;
}
public void sendMessageToActiveMQ(String queueName, String message) throws JMSException {
Destination destination = getDestination(queueName);
TextMessage toSend = session.createTextMessage(message);
producer.send(destination, toSend);
}
}
Related
I guess it is kind of a standard knowledge question, but will current_session run threaded or not?
ActiveMQSession current_session = (ActiveMQSession) connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Thread sessionThreads = new Thread(current_session);
sessionThreads.start();
If not, can please someone show me a code example how I run a session threaded?
What I want is concurrent sessions with producers/consumers which write/listen to their specific queues. I already tried to write a custom Thread by passing the connection to the thread, but when I created Producers I run into an error, 'that I can't start a Producer on an unregistered session'.
There is no constructor for java.lang.Thread that will accept a org.apache.activemq.ActiveMQSession object. Your code won't even compile let alone run.
Here's a simple client that will create threads for producing and consuming messages:
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.naming.Context;
import javax.naming.InitialContext;
public class MyMultiThreadedApp {
class MyConsumer extends Thread {
private final Connection connection;
private final Destination destination;
MyConsumer(Connection connection, Destination destination) {
this.connection = connection;
this.destination = destination;
}
#Override
public void run() {
try (Session session = connection.createSession(Session.AUTO_ACKNOWLEDGE)) {
MessageConsumer messageConsumer = session.createConsumer(destination);
connection.start();
Message message = messageConsumer.receive(5000);
if (message == null) {
System.out.println("Did not receive message within the allotted time.");
return;
}
System.out.println("Received message: " + message);
} catch (Throwable e) {
e.printStackTrace();
return;
}
}
}
class MyProducer extends Thread {
private final Connection connection;
private final Destination destination;
MyProducer(Connection connection, Destination destination) {
this.connection = connection;
this.destination = destination;
}
#Override
public void run() {
try (Session session = connection.createSession(Session.AUTO_ACKNOWLEDGE)) {
MessageProducer messageProducer = session.createProducer(destination);
messageProducer.send(session.createTextMessage("My message"));
System.out.println("Sent message");
} catch (Throwable e) {
e.printStackTrace();
return;
}
}
}
public static void main(String... args) throws Exception {
MyMultiThreadedApp myMultiThreadedApp = new MyMultiThreadedApp();
InitialContext initialContext = null;
initialContext = new InitialContext();
Queue queue = (Queue) initialContext.lookup("queue/exampleQueue");
ConnectionFactory cf = (ConnectionFactory) initialContext.lookup("ConnectionFactory");
Connection connection = cf.createConnection();
Thread myConsumer = myMultiThreadedApp.runConsumer(connection, queue);
Thread myProducer = myMultiThreadedApp.runProducer(connection, queue);
myConsumer.join();
myProducer.join();
}
private Thread runConsumer(Connection connection, Destination destination) {
MyConsumer myConsumer = new MyConsumer(connection, destination);
myConsumer.start();
return myConsumer;
}
private Thread runProducer(Connection connection, Destination destination) {
MyProducer myProducer = new MyProducer(connection, destination);
myProducer.start();
return myProducer;
}
}
This application receives & forwards messages from database events to client applications. Messages are immediately delivered when the client browser has a web socket session.
However, when no web socket session exists and a message is sent by the JMSProducer into the Destination "jms/notificationQueue" in QueueSenderSessionBean, the message is immediately consumed in NotificationEndpoint. This is not my intent.
My intent is for the queue to retain the message until the user connects to NotificationEndpoint. If the user is not connected to the NotificationEndpoint, I think there should be no instance of NotificationEndpoint created to receive the message.
How do I delay the JMSConsumer consuming the message from the queue?
Overview - TomEE Plus 8.0.0-M1 project
Application receives notification in a NotificationServlet
HttpServletRequest
String message is put into JMS Queue by QueueSenderSessionBean injected into NotificationServlet
NotificationMessageDrivenBean implements MessageListener to listen to the JMS Queue
An Event annotated with #NotificationServletJMSMessage is fired from NotificationMessageDrivenBean for an Observer in
NotificationEndpoint method onJMSMessage.
NotificationEndpoint uses PushContext which gathers all websocket sessions to deliver the message to the user
In PushContext.send, if any websocket sessions with a user uuid property matching the message user uuid property, the message is
delivered to each websocket session.
My understanding of #ServerEndpoint is that "each new WS session gets its own instance." Notify only specific user(s) through WebSockets, when something is modified in the database
Sources: the above link from https://stackoverflow.com/users/157882/balusc
and https://blogs.oracle.com/theaquarium/integrating-websockets-and-jms-with-cdi-events-in-java-ee-7-v2
WEB-INF/resources.xml
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<Resource id="jmsConnectionFactory" type="javax.jms.ConnectionFactory">
connectionMaxIdleTime = 15 Minutes
connectionMaxWaitTime = 5 seconds
poolMaxSize = 10
poolMinSize = 0
resourceAdapter = Default JMS Resource Adapter
transactionSupport = xa
</Resource>
</resources>
NotificationServlet.java
import java.io.IOException;
import java.util.UUID;
import javax.annotation.Resource;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.jms.Queue;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
#WebServlet("/notifications")
public class NotificationServlet extends HttpServlet
{
#Resource(name = "jms/notificationQueue")
private Queue _notificationQueue;
#Inject
private QueueSenderSessionBean _queueSessionSenderBean;
#Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException,
IOException
{
try
{
String notificationJson =
extractNotificationJson(request);
if (notificationJson != null)
{
_queueSessionSenderBean.sendMessage(
"notification="
+ notificationJson);
}
}
catch (Exception e)
{
e.printStackTrace();
// handle exception
}
}
public String extractNotificationJson(HttpServletRequest request)
throws IOException
{
if(request.getParameter("notification") != null)
{
String[] notificationString =
request.getParameterValues("notification");
return notificationString[0];
}
return null;
}
}
QueueSenderSessionBean.java
import javax.annotation.Resource;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.inject.Named;
import javax.jms.DeliveryMode;
import javax.jms.JMSConnectionFactory;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.JMSProducer;
import javax.jms.Queue;
import javax.jms.TextMessage;
import org.json.JSONObject;
#Named
#LocalBean
#Stateless
public class QueueSenderSessionBean
{
#Resource(mappedName = "jms/notificationQueue")
private Queue _notificationQueue;
#Inject
#JMSConnectionFactory("jmsConnectionFactory")
private JMSContext _jmsContext;
// Static Methods
// Member Methods
public void sendMessage(String message)
{
try
{
JMSProducer messageProducer =
_jmsContext.createProducer();
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
String userProperty = "someValue";
TextMessage textMessage = _jmsContext.createTextMessage(message);
textMessage.setStringProperty("userProperty", userProperty);
messageProducer.send(_notificationQueue, textMessage);
}
catch (JMSException e)
{
e.printStackTrace();
// handle jms exception
}
}
}
Qualifier NotificationServletJMSMessage.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public #interface NotificationServletJMSMessage
{
}
NotificationMessageDrivenBean.java
import javax.ejb.MessageDriven;
import javax.enterprise.event.Event;
import javax.inject.Inject;
import javax.inject.Named;
import javax.jms.Message;
import javax.jms.MessageListener;
#Named
#MessageDriven(mappedName = "jms/notificationQueue")
public class NotificationMessageDrivenBean implements MessageListener
{
#Inject
#NotificationServletJMSMessage
Event<Message> jmsEvent;
#Override
public void onMessage(Message message)
{
jmsEvent.fire(message);
}
}
PushContext.java
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.jms.JMSConsumer;
import javax.jms.JMSContext;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.TextMessage;
import javax.websocket.Session;
#ApplicationScoped
public class PushContext
{
#Inject
private JMSContext _jmsContext;
#Resource(mappedName = "jms/notificationQueue")
private Queue _notificationQueue;
private Map<String, Set<Session>> _sessions;
#PostConstruct
public void init()
{
_sessions = new ConcurrentHashMap<>();
}
public void add(Session session, String userUuid)
{
_sessions.computeIfAbsent(userUuid,
value -> ConcurrentHashMap.newKeySet()).add(session);
}
void remove(Session session)
{
_sessions.values().forEach(value -> value.removeIf(e -> e.equals(session)));
}
public void send(Set<String> userUuids, Message message) throws JMSException
{
String userUuid = message.getStringProperty("userUuid");
userUuids.add(userUuid);
Set<Session> userSessions;
synchronized(_sessions)
{
userSessions = _sessions.entrySet().stream()
.filter(e -> userUuids.contains(e.getKey()))
.flatMap(e -> e.getValue().stream())
.collect(Collectors.toSet());
}
for (Session userSession : userSessions)
{
if (userSession.isOpen())
{
userSession.getAsyncRemote().sendText(((TextMessage) message).getText());
}
}
}
public void removeSession(Session session)
{
String userUuid = (String)session.getUserProperties().get("userUuid");
_sessions.remove(userUuid, session);
}
}
NotificationEndpoint.java
import java.io.IOException;
import java.io.Serializable;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.inject.Named;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.websocket.CloseReason;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
#Named
#ServerEndpoint(value="/notificationEndpoint/{tokenId}")
public class NotificationEndpoint
{
private static final Set<Session> SESSIONS =
Collections.synchronizedSet(new HashSet<Session>());
private QueueSenderSessionBean _senderBean;
#Inject
private PushContext _pushContext;
#Inject
public NotificationEndpoint(QueueSenderSessionBean senderBean)
{
_senderBean = senderBean;
}
#OnOpen
public void onOpen(Session session,
EndpointConfig configurator,
#PathParam(value = "tokenId") String userUuidString)
{
session.getUserProperties().put("userUuid", userUuidString);
_pushContext.add(session, userUuidString);
}
#OnMessage
public void onMessage(String message, Session session)
throws IOException
{
System.out.println("Message received: " + message);
_senderBean.sendMessage(message);
}
#OnClose
public void onClose(CloseReason reason, Session session)
{
System.out.println(
"Closing 'notificatioEndpoint due to "
+ reason.getReasonPhrase());
try
{
session.close();
}
catch (IOException e)
{
e.printStackTrace();
}
_pushContext.removeSession(session);
}
#OnError
public void error(Session session, Throwable t)
{
t.printStackTrace();
}
public static void sendToAllClients(String message)
{
synchronized (SESSIONS)
{
for (Session session : SESSIONS)
{
if (session.isOpen())
{
session.getAsyncRemote().sendText(message);
}
}
}
}
public void onJMSMessage(#Observes #NotificationServletJMSMessage Message message)
{
Set<String> userUuids = new HashSet<String>();
try
{
_pushContext.send(userUuids, message);
}
catch (JMSException ex)
{
ex.printStackTrace();
Logger.getLogger(NotificationEndpoint.class.getName()).
log(Level.SEVERE, null, ex);
}
}
}
Thank you,
Ted S
Delayed message delivery accomplished inspired by the solution here.
The solution was using a local queue to hold messages if the user was not connected to the web socket, then when connected, moving messages from the local queue to a remote queue which gets received/consumed immediately using the MessageDrivenBean.
Also, instead of listening for messages from the database (Postgresql) with a Web Servlet, I changed my DB trigger to NOTIFY and started an asynchronous listener using the pgjdbc-ng driver and the Postgresql LISTEN/NOTIFY pattern described here.
NotificationListener.java
#Stateless
public class NotificationListener extends Thread
{
#Inject
private QueueSenderSessionBean _queueSessionSenderBean;
#Override
public void run()
{
listenToNotifications();
}
public void listenToNotifications()
{
PGNotificationListener listener = new PGNotificationListener()
{
public void notification(int processId, String channelName, String payload)
{
System.out.println("Received notification from: "
+ channelName + ", "
+ payload);
_queueSessionSenderBean.sendMessage(payload);
}
};
PGDataSource dataSource = new PGDataSource();
dataSource.setHost("localhost");
dataSource.setDatabase("db");
dataSource.setPort(5432);
dataSource.setUser("user");
dataSource.setPassword("pass");
try(PGConnection connection =
(PGConnection) dataSource.getConnection())
{
Statement statement = connection.createStatement();
statement.execute("LISTEN notifications");
statement.close();
connection.addNotificationListener(listener);
while (true)
{
if (Thread.currentThread().isInterrupted())
{
break;
}
}
}
catch (Exception e)
{
// TODO: handle exception
e.printStackTrace();
}
}
}
NotificationStarter.java
#Singleton
#Startup
public class NotificationsStarter
{
#EJB
private NotificationListener _listener;
#PostConstruct
public void startListener()
{
_listener.start();
}
#PreDestroy
public void shutdown()
{
_listener.interrupt();
}
}
PushContext.java
#ApplicationScoped
public class PushContext
{
#Resource(mappedName = "jms/localNotificationQueue")
private Queue _localNotificationQueue;
#Resource(mappedName = "jms/remoteNotificationQueue")
private Queue _remoteNotificationQueue;
private Map<String, Set<Session>> _sessions;
#PostConstruct
public void init()
{
_sessions = new ConcurrentHashMap<>();
}
public void add(Session session, String userUuid)
{
_sessions.computeIfAbsent(userUuid,
value -> ConcurrentHashMap.newKeySet()).add(session);
}
void remove(Session session)
{
_sessions.values().forEach(value -> value.removeIf(e -> e.equals(session)));
}
public void send(Set<String> userUuids, Message message) throws JMSException
{
String userUuid = message.getStringProperty("userUuid");
userUuids.add(userUuid);
Set<Session> userSessions;
synchronized(_sessions)
{
userSessions = _sessions.entrySet().stream()
.filter(e -> userUuids.contains(e.getKey()))
.flatMap(e -> e.getValue().stream())
.collect(Collectors.toSet());
for (Session userSession : userSessions)
{
if (userSession.isOpen())
{
userSession.getAsyncRemote().sendText(((TextMessage) message).getText());
}
}
}
}
public void removeSession(Session session)
{
String userUuid = (String)session.getUserProperties().get("userUuid");
_sessions.remove(userUuid, session);
}
public Boolean userHasWebSocketSession(String userUuid)
{
Boolean sessionOpen = false;
Set<String> userUuids = new HashSet<String>();
userUuids.add(userUuid);
Set<Session> userSessions;
synchronized(_sessions)
{
userSessions = _sessions.entrySet().stream()
.filter(e -> userUuids.contains(e.getKey()))
.flatMap(e -> e.getValue().stream())
.collect(Collectors.toSet());
}
for (Session userSession : userSessions)
{
if (userSession.isOpen())
{
sessionOpen = true;
break;
}
}
return sessionOpen;
}
}
QueueSenderSessionBean.java
#Named
#LocalBean
#Stateless
public class QueueSenderSessionBean
{
#Resource(mappedName = "jms/localNotificationQueue")
private Queue _localNotificationQueue;
#Resource(mappedName = "jms/remoteNotificationQueue")
private Queue _remoteNotificationQueue;
#Inject
#JMSConnectionFactory("jmsConnectionFactory")
private JMSContext _jmsContext;
#Inject
PushContext _pushContext;
public void sendMessage(String message)
{
JMSProducer messageProducer =
_jmsContext.createProducer();
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
try
{
String userProperty = "someValue";
TextMessage textMessage = _jmsContext.createTextMessage(message);
textMessage.setStringProperty("userProperty", userProperty );
Boolean userIsConnected =
_pushContext.userHasWebSocketSession(userUuid);
if (!userIsConnected)
{
messageProducer.send(_localNotificationQueue, textMessage);
}
else
{
messageProducer.send(_remoteNotificationQueue, textMessage);
}
}
catch (JMSException e)
{
e.printStackTrace();
}
}
}
NotificationMessageDrivenBean.java is now listening to only the remote queue
#Named
#MessageDriven(mappedName = "jms/remoteNotificationQueue")
public class NotificationMessageDrivenBean implements MessageListener
{
#Inject
#NotificationServletJMSMessage
Event<Message> jmsEvent;
#Override
public void onMessage(Message message)
{
jmsEvent.fire(message);
}
}
New QueueReceiverSessionBean.java is used to receive/consume messages from the localNotificationQueue and places them in the remoteNotificationQueue whenever the user connects to NotificationEndpoint web socket.
#Named
#LocalBean
#Stateless
public class QueueReceiverSessionBean
{
#Resource(mappedName = "jms/localNotificationQueue")
private Queue _localNotificationQueue;
#Resource(mappedName = "jms/remoteNotificationQueue")
private Queue _remoteNotificationQueue;
#Inject
#JMSConnectionFactory("jmsConnectionFactory")
private JMSContext _jmsContext;
public void receiveQueuedMessages(String userUuidString) throws JMSException
{
Set<String> userUuids =
new HashSet<String>();
userUuids.add(userUuidString);
JMSConsumer messageConsumer =
_jmsContext.createConsumer(_localNotificationQueue,
"userProperty='someValue'",
true);
JMSProducer messageProducer =
_jmsContext.createProducer();
Message localMessage =
messageConsumer.receive(10);
while(localMessage != null)
{
TextMessage textMessage =
_jmsContext.createTextMessage(((TextMessage) localMessage).getText());
textMessage.setStringProperty("userUuid", userUuidString);
messageProducer.send(_remoteNotificationQueue, textMessage);
localMessage.acknowledge();
localMessage =
messageConsumer.receive(10);
}
messageConsumer.close();
}
public void sendMessage(String message)
{
JMSProducer messageProducer =
_jmsContext.createProducer();
messageProducer.setDeliveryMode(DeliveryMode.PERSISTENT);
try
{
if (message.startsWith("notification"))
{
String messageJson = message.substring(message.indexOf("=") + 1);
JSONObject notificationJson =
new JSONObject(messageJson);
String userUuid = notificationJson.getString("receivinguseruuid");
TextMessage textMessage = _jmsContext.createTextMessage(message);
textMessage.setStringProperty("userUuid", userUuid);
messageProducer.send(_remoteNotificationQueue, textMessage);
}
}
catch (JMSException e)
{
e.printStackTrace();
}
}
}
NotificationEndpoint.java
#Named
#ServerEndpoint(value="/notificationEndpoint/{tokenId}")
public class NotificationEndpoint implements Serializable
{
private static final long serialVersionUID = 1L;
private static final Set<Session> SESSIONS =
Collections.synchronizedSet(new HashSet<Session>());
private QueueReceiverSessionBean _senderBean;
#Inject
private PushContext _pushContext;
#Inject
public NotificationEndpoint(QueueReceiverSessionBean senderBean)
{
_senderBean = senderBean;
}
#OnOpen
public void onOpen(Session session,
EndpointConfig configurator,
#PathParam(value = "tokenId") String userUuidString)
{
session.getUserProperties().put("userUuid", userUuidString );
_pushContext.add(session, userUuidString);
try
{
_senderBean.receiveQueuedMessages(userUuidString);
}
catch (JMSException e)
{
e.printStackTrace();
}
}
#OnMessage
public void onMessage(String message, Session session)
throws IOException
{
_senderBean.sendMessage(message);
}
#OnClose
public void onClose(CloseReason reason, Session session)
{
try
{
session.close();
}
catch (IOException e)
{
e.printStackTrace();
}
_pushContext.removeSession(session);
}
#OnError
public void error(Session session, Throwable t)
{
t.printStackTrace();
}
public static void sendToAllClients(String message)
{
synchronized (SESSIONS)
{
for (Session session : SESSIONS)
{
if (session.isOpen())
{
session.getAsyncRemote().sendText(message);
}
}
}
}
public void onJMSMessage(#Observes #NotificationServletJMSMessage Message message)
{
Set<String> userUuids = new HashSet<String>();
try
{
_pushContext.send(userUuids, message);
}
catch (JMSException ex)
{
ex.printStackTrace();
Logger.getLogger(NotificationEndpoint.class.getName()).
log(Level.SEVERE, null, ex);
}
}
}
Note: This code was used in the TomEE 8.0 container. Injecting JMSContext into the EJBs uncovered a bug in TomEE where the container fails to release the JMSConnection resource. Issue has been added to TomEE issues tracker
I have this sender class which is sending messages to myQueue.
Sender.java
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.commons.collections.Factory;
public class Sender {
private ConnectionFactory factory = null;
private Connection connection = null;
private Session session = null;
private Destination destination = null;
private MessageProducer producer = null;
String [] messages = new String[] {"Hello ...My name is vaniiiiiiiiiii tanejaaaaaaaaaaaaaa",
"Hello ...My name is priyanka tanejaaaaaaaaaaaaaa",
"Hello ...My name is rahul tanejaaaaaaaaaaaaaa",
"Hello ...My name is popo tanejaaaaaaaaaaaaaa"};
public void sendMessage(){
factory = new ActiveMQConnectionFactory(
"tcp://localhost:61619");
try {
connection= factory.createConnection();
connection.start();
session= connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
destination=session.createQueue("Parul");
producer = session.createProducer(destination);
TextMessage message = session.createTextMessage();
for(int i=0;i<messages.length;i++){
message.setText(messages[i]);
System.out.println("Sent: " + message.getText());
producer.send(message);
}
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
Sender sender = new Sender();
sender.sendMessage();
}
}
My message is getting published to activemq now we want to read messages from activemq. So i have written class Consumer.
Consumer.java
import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageListener;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.camel.Main;
public class Reciever implements MessageListener{
private ConnectionFactory factory;
private Connection connection;
private Session session;
private Destination destination;
private MessageConsumer consumer = null;
Reciever(){
}
void recieveMessage(){
factory= new ActiveMQConnectionFactory("tcp://localhost:61619");
try {
connection=factory.createConnection();
connection.start();
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
System.out.println("session started");
destination=session.createQueue("Parul");
consumer = session.createConsumer(destination);
System.out.println("Consumer created");
while (true) {
System.out.println("About to read");
Message msg = consumer.receive(5000);
if (msg instanceof TextMessage) {
TextMessage tm = (TextMessage) msg;
System.out.println(tm.getText());
}
else{
System.out.println("Queue Empty");
connection.stop();
break;
}
}
} catch (JMSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static void main(String[] args) {
Reciever reciever = new Reciever();
reciever.recieveMessage();
}
}
But my consumer is not able to read message from queue. What is the issue. Can you help me out in this.I want to read all 4 messsages at same time.
i am implementing the onMessgae () method of MessageListner Class but its not working even i am putting the debug point inside this method but debugger also not goes inside this method.
Use this method to receive the message from Queue.
Below is my code for receiving the message but its not giving any message when i am sending the message through sender class and its message goes to JMS Server and shows like this way on server
Name Server DestiCurrent MessagesCurrent MessagesPending MessagesReceived JMSServer-0 AdminServer 3 15 1 2
Code for Reciver:
{
import java.util.Hashtable;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class QueueReceive implements MessageListener {
public final static String Server="t3://localhost:7001";
public final static String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";
public final static String JMS_FACTORY = "CF1";
enter code here
public final static String QUEUE = "Queue1";
private QueueConnectionFactory qconFactory;
private QueueConnection qcon;
private QueueSession qsession;
private QueueReceiver qreceiver;
private Queue queue;
private boolean quit = false;
String s1 = "avanish";
public void init(Context ctx, String queueName) throws NamingException, JMSException {
qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
qcon = qconFactory.createQueueConnection();
qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
queue = (Queue) ctx.lookup(queueName);
qreceiver = qsession.createReceiver(queue);
qreceiver.setMessageListener(this);
qcon.start();
System.out.println(qreceiver.receive());
}
public void close() throws JMSException {
qreceiver.close();
qsession.close();
qcon.close();
}
public static void main(String[] args) throws Exception {
/*if (args.length != 1) {
System.out.println("Usage: java examples.jms.queue.QueueReceive WebLogicURL");
return;
}*/
InitialContext ic = getInitialContext();
QueueReceive qr = new QueueReceive();
qr.init(ic, QUEUE);
System.out.println("JMS Ready To Receive Messages (To quit, send a \"quit\" message).");
synchronized (qr) {
while (!qr.quit) {
try {
qr.wait();
} catch (InterruptedException ie) {
}
}
}
qr.close();
}
private static InitialContext getInitialContext() throws NamingException {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, Server);
return new InitialContext(env);
}
#Override
public void onMessage(Message msg) {
try {
String msgText;
if(msg instanceof TextMessage){
msgText = ((TextMessage) msg).getText();
}
else{
msgText = msg.toString();
}
System.out.println("reciver msg" + msgText);
if(msgText.equalsIgnoreCase("quit")){
synchronized (this) {
quit= true;
this.notifyAll();
}
}
} catch (JMSException e) {
System.err.println("Exception" + e.getMessage());
}
}
}
}
Code for Sender:
{
import javax.jms.JMSException;
import javax.jms.Queue;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Hashtable;
import javax.jms.*;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
public class QueueSend {
public final static String Server="t3://localhost:7001";
public final static String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";
public final static String JMS_FACTORY = "CF1";
public final static String QUEUE = "Queue1";
// public final static String QUEUE = "DQ";
private QueueConnectionFactory qconFactory;
private QueueConnection qcon;
private QueueSession qsession;
private QueueSender qsender;
private Queue queue;
private TextMessage msg;
public void init(Context ctx, String queueName) throws NamingException, JMSException
{
qconFactory = (QueueConnectionFactory) ctx.lookup(JMS_FACTORY);
qcon = qconFactory.createQueueConnection();
qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
queue = (Queue) ctx.lookup(queueName);
qsender = qsession.createSender(queue);
msg = qsession.createTextMessage();
qcon.start();
}
public void send(String message) throws JMSException {
msg.setText(message);
//msg.acknowledge();
qsender.setPriority(9);
qsender.send(msg);
}
public void close() throws JMSException {
qsender.close();
qsession.close();
qcon.close();
}
public static void main(String[] args) throws Exception {
InitialContext ic = getInitialContext();
QueueSend qs = new QueueSend();
qs.init(ic, QUEUE);
readAndSend(qs);
qs.close();
}
private static void readAndSend(QueueSend qs) throws IOException, JMSException
{
BufferedReader msgStream = new BufferedReader(new InputStreamReader(System.in));
String line = null;
boolean quitNow = false;
do {
System.out.print("Enter message (\"quit\" to quit): \n");
line = msgStream.readLine();
if (line != null && line.trim().length() != 0) {
qs.send(line);
System.out.println("JMS Message Sent: " + line + "\n");
quitNow = line.equalsIgnoreCase("quit");
}
} while (!quitNow);
}
private static InitialContext getInitialContext()
throws NamingException
{
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, Server);
return new InitialContext(env);
}
}
}
Insted of using MessageListener i used below lines of code and getting the exact message from queue
queue = (Queue) ctx.lookup(queueName);
qreceiver = qsession.createReceiver(queue);
message = qreceiver.receiveNoWait();
its successfully received the messages from Queue.
I've tested JMS messages sending serially and concurrently(5 threads send jms messages concurrently from the producer).
When I send 100 messages concurrently, few messages payload at the receiving end are NULL. When sent serially there is no issue.
Do I need to setup a session pool or use MDB at the consumer side to handle the messages concurrently? The setup of the JMS is good, because we are receiving messages. Am I missing anything here?
Short description of the pproject setup:
Publisher is a stateless session bean
Weblogic 8.1 jms server connection factory and destination are retrieved through
JNDI
Consumer is a java class which subscribes to this server JMS queue
and performs the tasks. (this is not a MDB or a Threaded class, listens to the queue
asynchronously)
EDITED
JmsConsumer
package net;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Hashtable;
import javax.jms.ExceptionListener;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueReceiver;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
public class ReadJMS implements MessageListener, ExceptionListener {
public final static String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";
public final static String PROVIDER_URL = "t3://address:7003";
public final static String JMS_FACTORY = "MSS.QueueConnectionFactory";
public final static String QUEUE = "jms.queue";
#SuppressWarnings("null")
public void receiveMessage() throws Exception {
// System.out.println("receiveMessage()..");
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, PROVIDER_URL);
// Define queue
QueueReceiver qreceiver = null;
QueueSession qsession = null;
QueueConnection qcon = null;
ReadJMS async = new ReadJMS();
try {
InitialContext ctx = new InitialContext(env);
QueueConnectionFactory qconFactory = (QueueConnectionFactory) ctx
.lookup(JMS_FACTORY);
qcon = qconFactory.createQueueConnection();
qcon.setExceptionListener(async);
qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = (Queue) ctx.lookup(QUEUE);
qreceiver = qsession.createReceiver(queue);
qreceiver.setMessageListener(async);
qcon.start();
System.out.println("readingMessage()..");
// TextMessage msg = (TextMessage) qreceiver.receive();
// System.out.println("Message read from " + QUEUE + " : "
// + msg.getText());
// msg.acknowledge();
} catch (Exception ex) {
ex.printStackTrace();
}
// } finally {
// if (qreceiver != null)
// qreceiver.close();
// if (qsession != null)
// qsession.close();
// if (qcon != null)
// qcon.close();
// }
}
public static void main(String[] args) throws Exception {
ReadJMS test = new ReadJMS();
System.out.println("init");
test.receiveMessage();
while (true) {
Thread.sleep(10000);
}
}
public void onException(JMSException arg0) {
System.err.println("Exception: " + arg0.getLocalizedMessage());
}
public synchronized void onMessage(Message arg0) {
try {
if(((TextMessage)arg0).getText() == null || ((TextMessage)arg0).getText().trim().length()==0){
System.out.println(" " + QUEUE + " : "
+ ((TextMessage) arg0).getText());
}
System.out.print(".");
PrintWriter out = new PrintWriter(new BufferedWriter(
new FileWriter("Output.txt", true)));
Date now = new Date();
out.println("message: "+now.toString()+ " - "+((TextMessage)arg0).getText()+"");
out.close();
} catch (JMSException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
WriteJms
package net;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Hashtable;
import javax.jms.Queue;
import javax.jms.QueueConnection;
import javax.jms.QueueConnectionFactory;
import javax.jms.QueueSender;
import javax.jms.QueueSession;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.naming.Context;
import javax.naming.InitialContext;
public class WriteJMS {
public final static String JNDI_FACTORY = "weblogic.jndi.WLInitialContextFactory";
public final static String PROVIDER_URL = "t3://url:7003";
public final static String JMS_FACTORY = "MSS.QueueConnectionFactory";
public final static String QUEUE = "jms.queue";
#SuppressWarnings("unchecked")
public void sendMessage() throws Exception {
#SuppressWarnings("rawtypes")
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, JNDI_FACTORY);
env.put(Context.PROVIDER_URL, PROVIDER_URL);
// Define queue
QueueSender qsender = null;
QueueSession qsession = null;
QueueConnection qcon = null;
try {
InitialContext ctx = new InitialContext(env);
QueueConnectionFactory qconFactory = (QueueConnectionFactory) ctx
.lookup(JMS_FACTORY);
qcon = qconFactory.createQueueConnection();
qsession = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
Queue queue = (Queue) ctx.lookup(QUEUE);
TextMessage msg = qsession.createTextMessage();
msg.setText("<eventMessage><eventId>123</eventId><eventName>123</eventName><documentNumber>123</documentNumber><customerId>123</customerId><actDDTaskDate>123</actDDTaskDate><taskStatusErrorMessage>123</taskStatusErrorMessage></eventMessage>");
qsender = qsession.createSender(queue);
qsender.send(msg);
System.out.println("Message [" + msg.getText()
+ "] sent to Queue: " + QUEUE);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (qsender != null)
qsender.close();
if (qsession != null)
qsession.close();
if (qcon != null)
qcon.close();
}
}
}
Reusing a session across multiple threads is notoriously forbidden. But you don't do this. You create everything (connection session and producer) anew for each message. That is inefficient, but not incorrect and should not cause these errors. The code you give us looks good to me.
I am a little surprised that no exceptions occur at the sending side. Can you give some more details about the JMS implementation? Perhaps there's more information in the message broker's log?
Have you counted the messages and does the number received equal the amount sent? Could someone else be sending messages to the same queue?