WS Server code:
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ejb.Local;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
#ServerEndpoint(value = "/websockets/broadcast-server")
#Local
public class BroadcastServer {
private static final Logger LOGGER = Logger.getLogger(BroadcastServer.class.getName());
private final Set<Session> sessions = new HashSet<>();
#OnMessage
public String onMessage(String message, Session session) throws IOException {
LOGGER.log(Level.INFO, "Received message: {0}", message);
broadcastMessage("!!!BROADCAST!!! " + message);
return "Echo: " + message;
}
#OnOpen
public void onOpen(Session session) throws IOException {
session.getBasicRemote().sendText("* The session is open.");
LOGGER.info("The session is open.");
this.sessions.add(session);
}
#OnClose
public void onClose(Session session) throws IOException {
LOGGER.info("The session is close.");
this.sessions.remove(session);
}
#OnError
public void onError(Session session, Throwable error) throws Exception {
session.getBasicRemote().sendText("* Error happen. " + error.getMessage());
LOGGER.warning("Error happen. " + error.getMessage());
}
public void broadcastMessage(String message) throws IOException {
LOGGER.info("Number of sessions: " + this.sessions.size());
for (Session session : this.sessions) {
session.getBasicRemote().sendText(message);
}
}
}
Unit test/WS client code:
public class BroadcastClientTest {
#Test
public void test() throws Exception {
BroadcastClient client = new BroadcastClient();
client.connect(URI.create("ws://localhost:7001/ee-ejb/websockets/broadcast-server"));
client.send("Hello World!");
Thread.sleep(Integer.MAX_VALUE);
}
}
If I run a few instances of above Unit test then each client sends a single message to the server and receive a single response.
But I expect to receive more messages because every message should be broadcasted to all clients.
It feels like for each client WebLogic creates separate WS server instance.
How to broadcast a message?
Broadcast in WebLogic was successfully done with following code:
#OnMessage
public void onMessage(String message, Session session) throws IOException {
for (Session sess : session.getOpenSessions()) {
sess.getBasicRemote().sendText("Echo: " + message);
}
}
Related
In my Spring project I'm using websocket client (org.java_websocket.client.WebSocketClient) It's worked as fine, main problem is in my code I can to get result with onMessage method but it's Asyc and I need result in Sync caller, other websocket clients are similar behavior too
Async is basically working concept in websocket
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.extensions.IExtension;
import org.java_websocket.handshake.ServerHandshake;
import org.java_websocket.protocols.IProtocol;
import org.java_websocket.protocols.Protocol;
public class EmptyClient extends WebSocketClient {
private final static Draft_6455 draft = new Draft_6455(Collections.<IExtension>emptyList(), Collections.<IProtocol>singletonList(new Protocol("my-protocol")));
private String request = null, response = null;
public EmptyClient(String serverUri, String request) throws URISyntaxException {
super(new URI(serverUri), draft);
this.request = request;
setConnectionLostTimeout(3000);
connect();
}
#Override
public void onOpen(ServerHandshake handshakedata) {
send(request);
System.out.println("new connection opened");
}
#Override
public void onClose(int code, String reason, boolean remote) {
System.out.println("closed with exit code " + code + " additional info: " + reason + " remote:" + remote);
}
#Override
public void onMessage(String message) {
this.response = message;
close();
}
#Override
public void onMessage(ByteBuffer message) {
System.out.println("received ByteBuffer");
}
#Override
public void onError(Exception ex) {
System.err.println("an error occurred:" + ex);
}
public String getResponse() {
return response;
}
public static void main(String[] args) throws URISyntaxException, ExecutionException, InterruptedException {
String res = new EmptyClient("ws://123.abc:8888/", "{\"command\":\"create\",\"id\":\"1234\"}").getResponse();
System.out.println("response : "+res);
}
}
at the frist I think I could to use Future<T> and ExecutorService but main problem is Future itself is Async and result by onMessage method is Async too
let me to show you how is changed my previous code :
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.extensions.IExtension;
import org.java_websocket.handshake.ServerHandshake;
import org.java_websocket.protocols.IProtocol;
import org.java_websocket.protocols.Protocol;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class EmptyClient extends WebSocketClient {
private final static Draft_6455 draft = new Draft_6455(Collections.<IExtension>emptyList(), Collections.<IProtocol>singletonList(new Protocol("my-protocol")));
private String request = null, response = null;
private ExecutorService executor = Executors.newSingleThreadExecutor();
public EmptyClient(String serverUri, String request) throws URISyntaxException {
super(new URI(serverUri), draft);
this.request = request;
setConnectionLostTimeout(3000);
connect();
}
#Override
public void onOpen(ServerHandshake handshakedata) {
send(request);
System.out.println("new connection opened");
}
#Override
public void onClose(int code, String reason, boolean remote) {
System.out.println("closed with exit code " + code + " additional info: " + reason + " remote:" + remote);
executor.shutdown();
}
#Override
public void onMessage(String message) {
this.response = message;
close();
}
#Override
public void onMessage(ByteBuffer message) {
System.out.println("received ByteBuffer");
}
#Override
public void onError(Exception ex) {
System.err.println("an error occurred:" + ex);
}
public Future<String> getResponse() {
return executor.submit(() -> {
return response;
});
}
public static void main(String[] args) throws URISyntaxException, ExecutionException, InterruptedException {
Future<String> res = new EmptyClient("ws://123.abc:8888/", "{\"command\":\"create\",\"id\":\"1234\"}").getResponse();
System.out.println(res.isDone() + res.get());
}
}
I can not to use Future as return type of onMessage method because it's belong to WebSocketClient class that I used
we know by get method from Future class is right way to get result, if any method by Furter was exist that I could to call it in onMessage method when response is exposed then get method returned actual Furtur<String> Object but there is nothing to do, maybe another concurrency class or mixed technique are better to use
hence I call EmptyClient class in sync method and I need that result to produce something else, how can I to resolve it ? maybe other Concurrency API classes and or locker is there to use
I run simple websocket server on Tomcat:
package server.ws;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.server.ServerEndpoint;
#ServerEndpoint("/websocketendpoint")
public class WsServer {
#OnOpen
public void onOpen(){
System.out.println("Open Connection ...");
}
#OnClose
public void onClose(){
System.out.println("Close Connection ...");
}
#OnMessage
public String onMessage(String message){
System.out.println("Message from the client: " + message);
String echoMsg = "Echo from the server : " + message;
return echoMsg;
}
#OnError
public void onError(Throwable e){
e.printStackTrace();
}
}
Client that connects to my server complains regarding Tomcat closes session after echo was transmitted. How to make the server to not close the connection after echo?
i'm not sure, but i guess onMessagehas no return value.
for me, the following code works fine and i have any problems
#OnMessage
public void message(final String message, final javax.websocket.Session session) throws IOException {
for (final Session s : session.getOpenSessions()) {
final Basic endpoint = s.getBasicRemote();
endpoint.sendText(message);
}
}
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 simple smack 3.2.1 application. It connect to xmpp server
and waiting for another user conversation initiation. When user ask question
via chat, application send response (answer). And it is working fine. Here is code:
JabberApplication.java:
package jabberapplication;
import org.jivesoftware.smack.XMPPException;
public class JabberApplication {
public static void main(String[] args) throws XMPPException, InterruptedException {
String username = "USERNAME";
String password = "PASSWORD";
String server = "SERVER";
int port=5222;
XmppManager xmppManager = new XmppManager(server, port);
xmppManager.init();
xmppManager.performLogin(username, password);
xmppManager.setStatus(true, "Hello everyone");
boolean isRunning = true;
while (isRunning) {
Thread.sleep(50);
}
xmppManager.destroy();
}
}
XmppManager.java:
package jabberapplication;
import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.MessageListener;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Presence;
import org.jivesoftware.smack.packet.Presence.Type;
public class XmppManager {
private final String server;
private final int port;
private XMPPConnection connection;
private ChatManager chatManager;
private MessageListener messageListener;
private ConversationController conversationController;
public XmppManager(String server, int port) {
this.server = server;
this.port = port;
}
public void init() throws XMPPException {
System.out.println(String.format("Initializing connection to server %1$s port %2$d", server, port));
ConnectionConfiguration connectionConfiguration = new ConnectionConfiguration(server, port);
connection = new XMPPConnection(connectionConfiguration);
connection.connect();
System.out.println("Connected: " + connection.isConnected());
chatManager = connection.getChatManager();
chatManager.addChatListener(new MyChatManagerListener());
messageListener = new MyMessageListener();
conversationController = new ConversationController();
}
public void performLogin(String username, String password) throws XMPPException {
if (connection != null && connection.isConnected()) {
connection.login(username, password);
}
}
public void setStatus(boolean available, String status) {
Presence.Type type = available ? Type.available : Type.unavailable;
Presence presence = new Presence(type);
presence.setStatus(status);
connection.sendPacket(presence);
}
public void destroy() {
if (connection != null && connection.isConnected()) {
connection.disconnect();
}
}
public void sendMessage(String message, String buddyJID) throws XMPPException {
System.out.println(String.format("Sending mesage '%1$s' to user %2$s", message, buddyJID));
Chat chat = chatManager.createChat(buddyJID, messageListener);
chat.sendMessage(message);
}
class MyMessageListener implements MessageListener {
#Override
public void processMessage(Chat chat, Message message) {
String from = message.getFrom();
String body = message.getBody();
if (!body.equals("null")) {
System.out.println(String.format("Received message '%1$s' from %2$s", body, from));
try {
chat.sendMessage(conversationController.getAnswer(body));
} catch (XMPPException ex) {
System.out.println(ex.getMessage());
}
}
}
}
class MyChatManagerListener implements ChatManagerListener {
#Override
public void chatCreated(Chat chat, boolean bln) {
int indexAt = chat.getParticipant().indexOf("#");
String username = chat.getParticipant().substring(0, indexAt);
chat.addMessageListener(messageListener);
try {
chat.sendMessage("Hello " + username + " !");
} catch (XMPPException ex) {
System.out.println(ex.getMessage());
}
}
}
}
Well, the question is: How to write similar app using smack library version 4.2.3. It looks like that in 4.2.3 there are no MessageListener and ChatManagerListener classes. Any suggestions ?
Best Regards.
For Smack 4.2.3, ChatManagerListener interface is under org.jivesoftware.smack.chat class (note : chat not Chat).
So, you need to change import to org.jivesoftware.smack.chat.ChatManagerListener.
Also, MessageListener is now change to ChatMessageListener also under org.jivesoftware.smack.chat class.
So, you need to change import to org.jivesoftware.smack.chat.ChatMessageListener.
And rename the implements to as below :
class MyMessageListener implements ChatMessageListener
I'm working on MQTT protocol and I try to publish and subscribe with 2 distincts Java applications.
My first application is "Publish". I'm publish on a MQTT server a message.
My second application is "Subscribe". I'm subscribe to a topic and try to receive the message. But I never receive the message.
When I run the 2 applications, I begin with the "Subscribe" application and after I run the "Publish" application. When the "Publish" application begin, I lose my connection to the "Subscribe" application and I can't receive my message.
In the "Subscribe" application, my method messageArrived() is never called by client.setCallback(this). (See the code below).
Here is my 2 code application :
Publish Application :
Class PubClient :
package publishclient;
import java.util.Random;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
public class PubClient implements MqttCallback {
MqttClient client;
MqttConnectOptions connOpt;
Random rand = new Random();
int nbRandom = 0;
int valMax =151, valMin = 40;
public PubClient() throws MqttException {
String broker = "tcp://88.177.147.17:1883"; // Adress MQTT Server
String clientId = "0bdd-4445-82f3-928f8ddb1887"; // ClientID
String topic1f = "ApplicationRio/capteur"; // Topic
int QoSserveur = 2;
try {
String uuid = "ac8da3c6-0bdd-4445-82f3-928f8ddb3294";
MemoryPersistence persistence = new MemoryPersistence();
// Create 2 objects : client and connOpt
client = new MqttClient(broker, clientId, persistence);
connOpt = new MqttConnectOptions();
connOpt.setCleanSession(true);
client.setCallback(this);
// Connection to MQTT server
System.out.println("Connexion a : " + broker + " Publisher");
client.connect(connOpt);
//Create random number for my message
nbRandom = valMin + rand.nextInt(valMax-valMin);
System.out.println("nb aleatoire = " + nbRandom);
String messageAEnvoyer = uuid + "//" + nbRandom;
System.out.println("Message a envoyer : " + messageAEnvoyer);
MqttMessage message = new MqttMessage();
message.setPayload(messageAEnvoyer.getBytes());
message.setQos(QoSserveur);
client.publish(topic1f, message);
} catch(MqttException e) {
e.printStackTrace();
}
}
#Override
public void connectionLost(Throwable thrwbl) {System.out.println("Perdue connexion");}
#Override
public void messageArrived(String string, MqttMessage mm) throws Exception {
System.out.println("Message recu est : "+ new String(mm.getPayload()));}
#Override
public void deliveryComplete(IMqttDeliveryToken imdt) {
System.out.println("Message delivre au broker");
}
}
The main (Publish) :
package publishclient;
import org.eclipse.paho.client.mqttv3.MqttException;
public class PublishClient {
public static void main(String[] args) throws MqttException {
PubClient publieur = new PubClient();
}
The "subscribe" application :
Class SubClient :
package subscribeclient;
import org.eclipse.paho.client.mqttv3.MqttClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttCallback;
import org.eclipse.paho.client.mqttv3.MqttMessage;
import org.eclipse.paho.client.mqttv3.MqttException;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
public class SubClient implements MqttCallback {
MqttClient clientsub;
MqttConnectOptions connOpt;
public SubClient() throws MqttException{
String broker = "tcp://88.177.147.17:1883"; // Adress MQTT Server
String clientId = "0bdd-4445-82f3-928f8ddb1887"; // ClientID
String topic1f = "ApplicationRio/capteur"; // Topic
int QoSserveur = 2;
try{
// Create 2 objects : client and connOpt
clientsub = new MqttClient(broker, clientId);
connOpt = new MqttConnectOptions();
connOpt.setCleanSession(false);
connOpt.setKeepAliveInterval(30);
clientsub.setCallback(this);
// Connection to MQTT Server
System.out.println("Connexion a : " + broker + " Subscriber");
clientsub.connect(connOpt);
clientsub.subscribe(topic1f,QoSserveur);
} catch(MqttException e){
e.printStackTrace();
}
}
#Override
public void connectionLost(Throwable thrwbl) {
System.out.println("Connexion perdue");
}
#Override
public void messageArrived(String string, MqttMessage message) throws Exception {
System.out.println("Le message recu est : " + new String(message.getPayload()));
}
#Override
public void deliveryComplete(IMqttDeliveryToken imdt) {
System.out.println("Message arrive");
}
}
The main (Subscribe) :
package subscribeclient;
import org.eclipse.paho.client.mqttv3.MqttException;
public class SubscribeClient {
public static void main(String[] args) throws MqttException {
SubClient subscriber = new SubClient();
}
}
My 2 applications need to run in the same time, and I don't need to disconnect because I run applications all the time.
So, have you got an idea of why my "Subscribe Client" disconnect when I run the "Publish Client" and why I can't receive my message on my "Subscribe Message" ?
I use org.eclipse.paho.client.mqttv3-1.0.2.jar for the library for MQTT.
Client IDs have to be unique between ALL clients. You have used the same client ID for the publisher and subscriber so the broker will kick the subscriber off when the publisher connects.