I want to send a message to all active clients.
#OnMessage
public void onMessage(String message, Session session) {
switch (message) {
case "latencyEqualize":
for (Session otherSession : session.getOpenSessions()) {
RemoteEndpoint.Basic other = otherSession.getBasicRemote();
String data = "Max latency = "
+ LatencyEqualizer.getMaxLatency(latencies);
try {
other.sendText(data);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
break;
default:
RemoteEndpoint.Basic other = session.getBasicRemote();
try {
other.sendText(message);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
Something is wrong with this code. When i send message "latencyEqualize" from the first client the server answers only to the same client. Other clients don't receive message "Max latency = 15". But when the second client sends to server any message, he recieves back "Max latency = 15". And all future calls to server return the message from previous call.
Is there a way to avoid this. I want all clients get "Max latency" message when one of them send "latencyEqualize" message to the server.
The reason why only one client receives your message is that session variable contains connection only of that client who sent you message.
To send your message to all clients, store their connections in some collection (for example, ArrayList<Session>) in onOpen() method, and then iterate though that collection to get connections of all of your clients
Related
I have deployed my Java-MDB based application using ActiveMQ as messaging service . I could see that a few messages have been in pending status for quite some time on some queues. I have read that this happens when ActiveMQ delivers the message and consumer consumes the message but doesn't send the ack back. But I could not see any related loggers on the consumer/application side which proves that the message is consumed.
Could anyone please help me understand the reason of message being stuck in pending state.
Edit - Adding the details:
We are using Auto-acknowledge as acknowledgeMode and below is the onMessage method used on consumer side.
public void onMessage(Message message) {
try {
// Clear all ThreadLocal in SQLQueryHelper.
SQLQueryHelper.clearCache();
String messageOut = processMessage(message);
// if there is a reply, send it out
if (messageOut != null) {
logger.warn(LoggerKeys.LOG_1_ARGS,
new String[] {"Reply from MDB not supported. " + messageOut});
}
} catch (Throwable e) {
logger.error(LoggerKeys.LOG_1_ARGS,
new String[] {"Error encountered: " + e.toString()});
try {
//put message on error queue
handleError(message, e);
} catch (Throwable e2) {
//retry to put message on error queue
handleErrorAndRollBack(message, e2);
}
}
}
I encountered a knotty problem when receiving message from WildFly JMS queue. My code is below:
Session produceSession = connectionFactory.createConnection().createSession(false, Session
.CLIENT_ACKNOWLEDGE);
Session consumerSession = connectionFactory.createConnection().createSession(false, Session
.CLIENT_ACKNOWLEDGE);
ApsSchedule apsSchedule = new ApsSchedule();
boolean success;
MessageProducer messageProducer = produceSession.createProducer(outQueueMaxusOrder);
success = apsSchedule.sendD90Order(produceSession,messageProducer, d90OrderAps);
if (!success) {
logger.error("Can't send APS schedule msg ");
} else {
MessageConsumer consumer = consumerSession.createConsumer(inQueueDeliveryDate);
data = apsSchedule.receiveD90Result(consumerSession,consumer);
}
then getting into the receiveD90Result():
public DeliveryData receiveD90Result(Session session, MessageConsumer consumer) {
DeliveryData data = null;
try {
Message message = consumer.receive(10000);
if (message == null) {
return null;
}
TextMessage msg = (TextMessage) message;
String text = msg.getText();
logger.debug("Receive APS d90 result: {}", text);
ObjectMapper mapper = new ObjectMapper();
data = mapper.readValue(text, DeliveryData.class);
} catch (JMSException je) {
logger.error("Can't receive APS d90 order result: {}", je.getMessage());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
consumer.close();
} catch (JMSException e) {
e.printStackTrace();
}
}
return data;
}
But when implementing the consumer.receive(10000), the project can't get a message from queue. If I use asynchronous way of MDB to listen the queue, I can get the message from queue. How to resolve it?
There are multiple modes you can choose to get a message from the queue. Message Queues are by default asynchronous in usage. There are however cases when you want to read it synchronously , for example sending a message with account number and using another queue to read the response and match it with a message id or a message correlation id. When you do a receive , the program is waiting for a message to arrive within that polling interval specified in receive.
The code snippet you have , as i see it uses the psuedo synchronous approach. If you have to use it as an MDB , you will have to implement message driven bean (EJB Resource) or message listener.
The way that MDB/Message Listener works is more event based , instead of a poll with a timeout (like the receive) , you implement a callback called onMessage() that is invoked every time there is a message. Instead of a synchronous call , this becomes asynchronous. Your application may require some changes both in terms of design.
I don't see where you're calling javax.jms.Connection.start(). In fact, it doesn't look like you even have a reference to the javax.jms.Connection instance used for your javax.jms.MessageConsumer. If you don't have a reference to the javax.jms.Connection then you can't invoke start() and you can't invoke close() when you're done so you'll be leaking connections.
Furthermore, connections are "heavy" objects and are meant to be re-used. You should create a single connection for both the producer and consumer. Also, if your application is not going to use the javax.jms.Session from multiple threads then you don't need multiple sessions either.
Have implemented gcm ccs for chat module and i am able to send and receive messages. Below is the main connection module,
config = XMPPTCPConnectionConfiguration.builder()
.setServiceName("gcm-pesu.googleapis.com")
.setPort(GCM_PORT)
.setHost(GCM_SERVER)
.setCompressionEnabled(false)
.setConnectTimeout(30000)
.setSecurityMode(SecurityMode.ifpossible)
.setSendPresence(false)
.setSocketFactory(SSLSocketFactory.getDefault())
.build();
connection = new XMPPTCPConnection(config);
connection.connect();
Roster roster = Roster.getInstanceFor(connection);
roster.setRosterLoadedAtLogin(false);
connection.addConnectionListener(new LoggingConnectionListener());
// Handle incoming packets
connection.addAsyncStanzaListener(new MyStanzaListener(), new MyStanzaFilter());
// Log all outgoing packets
connection.addPacketInterceptor(new MyStanzaInterceptor(), new MyStanzaFilter());
connection.login(mProjectId + "#gcm.googleapis.com", mApiKey);
logger.info("logged in: " + mProjectId);
PingManager pm = PingManager.getInstanceFor(connection);
pm.setPingInterval(300);
pm.pingMyServer();
pm.registerPingFailedListener(new PingFailedListener() {
#Override
public void pingFailed() {
connection.disconnect();
logger.error("GCM CCS, Ping failed !!");
}
});
The problem i am running into is not receiving any message from GCM, sent by client device after a while. Though, the heartbeat looks normal and i do get pong from GCM even in that case. Is it something to do with SSL ?
Have handled connection draining case as follows,
String controlType = (String) jsonObject.get("control_type");
volatile boolean connectionDraining = false;
if ("CONNECTION_DRAINING".equals(controlType)) {
connectionDraining = true;
try {
connection.disconnect();
connect();
connectionDraining = false;
} catch (Exception e) {
logger.error("Error establishing new connection after draining ", e);
}
}
Implemented queue of channels when one of it is draining.
private Deque<Channel> channels;
protected void handleControlMessage(Map<String, Object> jsonObject) {
logger.info("Control message : " + jsonObject);
String controlType = (String) jsonObject.get("control_type");
if ("CONNECTION_DRAINING".equals(controlType)) {
connectionDraining = true;
}
}
Create new channel while sending message
public void sendDownstreamMessage(String jsonRequest) {
Channel channel = channels.peekFirst();
try {
if (channel.connectionDraining) {
synchronized (channels) {
channel = channels.peekFirst();
if (channel.connectionDraining) {
channels.addFirst(connect());
channel = channels.peekFirst();
}
}
}
channel.send(jsonRequest);
} catch (Exception e) {
logger.error("Message not sent. Error in connecting :", e);
}
}
GCM will take care of closing the other. This resolved the issue.
I believe you're facing a common case using gcm css that is not very visible in the documentation.
If you look in the doc, Control Messages you'll read:
Periodically, CCS needs to close down a connection to perform load balancing. Before it closes the connection, CCS sends a CONNECTION_DRAINING message to indicate that the connection is being drained and will be closed soon. "Draining" refers to shutting off the flow of messages coming into a connection, but allowing whatever is already in the pipeline to continue. When you receive a CONNECTION_DRAINING message, you should immediately begin sending messages to another CCS connection, opening a new connection if necessary. You should, however, keep the original connection open and continue receiving messages that may come over the connection (and ACKing them)—CCS handles initiating a connection close when it is ready.
I have a background service that runs in its own separate process using
android:process=":deamon"
In the manifest entry for the service. I want to communicate with the the service (remote process) from my activity and receive data from it.
I'm doing that by sending messages to and from the remote process as described in http://developer.android.com/guide/components/bound-services.html#Messenger and as they suggested I followed
If you want the service to respond, then you need to also create a Messenger in the client. >Then when the client receives the onServiceConnected() callback, it sends a Message to the >service that includes the client's Messenger in the replyTo parameter of the send() method.
The thing is, I need to provide a blocking/synchronous API to get data from my remote service, how can my "get" function block the caller and then return the data received in my incoming Handler ?
What would be the best approach to do that ?
This is code for messaging part of Client
SparseArray<CountDownLatch> lockArray = new SparseArray<>();
SparseArray<Bundle> msgDataArray = new SparseArray<>();
public Bundle sendAndWaitResponse(Message msg) throws
RemoteException, InterruptedException {
int msgId = msg.arg2;
Log.d("PlatformConnector", "Sending message to service, Type: "
+ msg.what + ", msgId: " + msg.arg2);
CountDownLatch latch = new CountDownLatch(1);
lockArray.put(msgId, latch);
platformMessenger.send(msg);
latch.await();
Bundle response = msgDataArray.get(msgId);
lockArray.delete(msgId);
msgDataArray.delete(msgId);
return response;
}
void storeResponseAndNotify(Message msg) {
int msgId = msg.arg2;
// Because the message itself is recycled after Handler returns,
// we should store only the data of message
msgDataArray.put(msgId, msg.getData());
lockArray.get(msgId).countDown();
}
private class ClientMessageHandler extends Handler {
#Override
public void handleMessage(Message msg) {
storeResponseAndNotify(msg);
}
}
This is example of utilizing above code.
RandomInt.getNextInt() is my custom static method, which generates random integer with Random.nextInt().
public JSONObject doSomething(JSONObject object) {
Message msg = Message.obtain(null, Constants.MESSAGE_SOMETHING, 0, RandomInt.getNextInt());
Bundle bundle = new Bundle();
bundle.putString(Constants.MESSAGE_DATA_SOMETHING, object.toString());
msg.setData(bundle);
try {
Bundle responseData = sendAndWaitResponse(msg);
return new JSONObject(responseData.getString(Constants.MESSAGE_DATA_RETURN));
} catch (RemoteException e) {
Log.e(TAG, "Failed to send message to platform");
e.printStackTrace();
} catch (InterruptedException e) {
Log.e(TAG, "Interrupted while waiting message from platform");
e.printStackTrace();
} catch (JSONException e) {
e.printStackTrace();
}
return null;
}
Sequence is as follows,
The Client prepares Message and set its arg2 as some random integer
(this integer will be the message id for synchronization).
The Client prepares new CountDownLatch and put it to LockArray.
the Client sends message with sendAndWaitResponse(). It sends message to service via Messenger and invokes latch.await().
Service processes receives message and prepare reply message. The arg2 of this reply message should be same as received message.
Service sends reply message to client via Messenger in replyTo.
Client message handler handles the message with storeResponseAndNotify.
When the blocking of Client thread is finished, the response data would be already prepared in msgDataArray.
CountDownLatch is simple switch to block and unblock the thread.
(http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html)
SparseArray is similar to HashMap, but more memory-efficient for smaller sets.
(http://developer.android.com/reference/android/util/SparseArray.html)
Be careful not to block the thread of Messenger. Messenger runs in single thread and if you block from the handleMessage(), it will block all other messages and cause deaklock problem.
I'm currently using a Java implementation of the Reliable UDP protocol, found here. The project has absolutely no tutorials so I have found it really hard to identify problems.
I have set up a client and server. The server runs on localhost:1234 and the client runs on localhost:1235. The server is first established, and loops listening for connections -
try {
ReliableSocket clientSocket = server.socket.accept();
InetSocketAddress clientAddress = (InetSocketAddress) clientSocket.getRemoteSocketAddress();
Logger.getLogger("ServerConnectionListener").info("New Connection from "+
clientAddress.getHostName()+":"+clientAddress.getPort()+" Processing...");
LessurConnectedClient client = new LessurConnectedClient(clientSocket);
ClientCommunicationSocketListener listener = new ClientCommunicationSocketListener(this, client);
clientSocket.addListener(listener);
} catch (Exception e) {
e.printStackTrace();
}
When a connection is established, it creates a listener for events on that socket -
class ClientCommunicationSocketListener implements ReliableSocketListener {
ServerConnectionListener connectionListener;
LessurConnectedClient client;
public ClientCommunicationSocketListener(ServerConnectionListener connectionListener, LessurConnectedClient client){
this.connectionListener = connectionListener;
this.client = client;
}
#Override
public void packetReceivedInOrder() {
connectionListener.server.handlePacket(client);
}
#Override
public void packetReceivedOutOfOrder() {
connectionListener.server.handlePacket(client);
}
}
When a packet is received, it passes it to server.handlePacket, which performs a debug routine of printing "Packet Received!".
My client connects to the server as so -
LessurClient client = new LessurClient();
InetSocketAddress a = (InetSocketAddress) server.getSocket().getLocalSocketAddress();
Logger.getLogger("client-connector").info("Trying to connect to server "+
a.getAddress().toString()+":"+
a.getPort());
client.connect(a.getAddress(), a.getPort());
// LessurClient.connect
public void connect(InetAddress address, int port){
try {
socket = new ReliableSocket(address, port, InetAddress.getLocalHost(), 1235);
isConnected = true;
Logger.getLogger("LessurClient").info("Connected to server "+address.getHostAddress()+":"+port);
} catch (IOException e) {
e.printStackTrace();
}
}
I have linked my code so when I press the key 'Z', it will send a packet to the server as so -
public void sendPacket(GamePacket packet){
if(!isConnected){
Logger.getLogger("LessurClient").severe("Can't send packet. Client is not connected to any server.");
return;
}
try {
OutputStream o = socket.getOutputStream();
o.write(packet.getData());
o.flush();
Logger.getLogger("LessurClient").info("Sending Packet with data \""+packet.getData()+"\" to server "+socket.getInetAddress().toString()+":"+socket.getPort());
} catch (IOException e) {
e.printStackTrace();
}
}
My problem is, after sending 32 packets, the server no longer receives packets, and after sending 64 packets, it crashes. I have investigated into the code, and it appears that its something associated with packets not being removed from the receive queue, as when I changed the _recvQueueSize variable in ReliableSocket.java:1815 from 32 to 40, I could now send 40 packets without something going wrong.
Could someone help me identify this issue? I've been looking at the code all day.
I managed to fix the problem.
You see, since this is an implementation of RUDP, it extends most of the Socket classes. Specifically, ReliableSocket.getInputStream(), was custom coded to a managed input stream. My problem was, I was receiving the packets, but not reading from the buffer.
When you receive a packet you're supposed to read from the buffer, otherwise the packet will not be dropped from the queue.
So all I had to do, was everytime I received a packet, read the size of the packet, and continue.