get result from websocket async call in sync call with concurrency classes - java

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

Related

JMS-Websocket - delayed message delivery

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

Retrofit 2.0 value from asynch thread

I am new into threads. And the question is: Is this correct way to extract values from async Retrofit thread. And why dont I get "0 code exit"?
Here is main code
import com.google.gson.GsonBuilder;
import iceandfire.Callbacks.RootCallback;
import iceandfire.Models.Root;
import iceandfire.Utils.IceFireClient;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class Main implements RootCallback
{
public static void main(String [] args)
{
RequestRoot(new Main());
//this imitates UI thread
Thread thread = new Thread()
{
public void run()
{
int counter = 5;
while (counter >= 0)
{
System.out.println("I am not retrofit Call");
counter -= 1;
try
{
Thread.sleep(1000); // 1 second
} catch (Exception e)
{
e.printStackTrace();
}
}
}
};
thread.start();
}
public static void RequestRoot(final RootCallback rootCallback)
{
Retrofit.Builder retroBuilder = new Retrofit.Builder()
.baseUrl("https://anapioficeandfire.com/")
.addConverterFactory(GsonConverterFactory.create());
Retrofit retrofit = retroBuilder.build();
IceFireClient client = retrofit.create(IceFireClient.class);
Call<Root> kall = client.root();
kall.enqueue(new Callback<Root>()
{
#Override
public void onResponse(Call<Root> call, Response<Root> response)
{
Root bloodyRoot = response.body();
rootCallback.onSuccess(bloodyRoot);
}
#Override
public void onFailure(Call<Root> call, Throwable t)
{
}
});
}
#Override
public void onSuccess(Root bloodyRoot)
{
System.out.println( new GsonBuilder().setPrettyPrinting().create().toJson(bloodyRoot).toString());
}
}
And callback
package iceandfire.Callbacks;
import iceandfire.Models.Root;
public interface RootCallback
{
void onSuccess(Root bloodyRoot);
}
What I expect is: 5x messages "I am not Retrofit call";Json that I get from iceandfireapi/root at any random time before, after, or between previous messages. And the last but not least ending of everything:"Process finished with exit code 0"
But what I get is an infinite loop: 5x messages, Json and NO exit message.
P.S. Timer thread is used to "imitate" some UI things. While retrofit is doing requests.

Java Websocket closes immediately

I am trying to use TooTallNate's Java-Websocket to connect to OkCoin. I found this simple code example somewhere, but I can't get it to work. The connection is immediately closed and so the call mWs.send(...) throws a WebsocketNotConnectedException. I can't figure out why; so far I have found a number of similar questions, none of which have an answer.
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.json.JSONObject;
import java.net.URI;
import java.net.URISyntaxException;
public class TestApp {
public static void main(String[] args) {
try {
URI uri = new URI("wss://real.okcoin.cn:10440/websocket/okcoinapi");
final WebSocketClient mWs = new WebSocketClient(uri) {
#Override
public void onMessage(String message) {
JSONObject obj = new JSONObject(message);
}
#Override
public void onOpen(ServerHandshake handshake) {
System.out.println("opened connection");
}
#Override
public void onClose(int code, String reason, boolean remote) {
System.out.println("closed connection");
}
#Override
public void onError(Exception ex) {
ex.printStackTrace();
}
};
mWs.connect();
JSONObject obj = new JSONObject();
obj.put("event", "addChannel");
obj.put("channel", "ok_btccny_ticker");
mWs.send(obj.toString());
} catch (URISyntaxException e) {
System.err.println("URI not formatted correctly");
}
}
}
Use mWs.connectBlocking() instead of mWs.connect() with this it will not close automatically.
See

Netty server with open http connections limit

I'm new to Netty and I wrote based on an example I found a Netty http server, that keeps http connections open to send server-sent-events to the browser client.
Problem is that it only accepts up to about ~5 connections and after that blocks new connections. I googled and found most answers said to set SO_LOGBACK to a higher value. Tried different values and while I saw no difference. I even set it to MAX_INTEGER value and still had only 5 connections.
Server code (Using Netty version 4.1.6.Final):
package server;
import static io.netty.buffer.Unpooled.copiedBuffer;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.HttpVersion;
public class NettyHttpServer {
private ChannelFuture channel;
private final EventLoopGroup masterGroup;
public NettyHttpServer() {
masterGroup = new NioEventLoopGroup(100);
}
public void start() {
try {
final ServerBootstrap bootstrap = new ServerBootstrap().group(masterGroup)
.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer < SocketChannel > () {
#Override
public void initChannel(final SocketChannel ch) throws Exception {
ch.pipeline().addLast("codec", new HttpServerCodec());
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(512 * 1024));
ch.pipeline().addLast("request", new ChannelInboundHandlerAdapter() {
#Override
public void channelRead(final ChannelHandlerContext ctx, final Object msg)
throws Exception {
System.out.println(msg);
registerToPubSub(ctx, msg);
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.writeAndFlush(new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.INTERNAL_SERVER_ERROR,
copiedBuffer(cause.getMessage().getBytes())));
}
});
}
}).option(ChannelOption.SO_BACKLOG, Integer.MAX_VALUE)
.childOption(ChannelOption.SO_KEEPALIVE, true);
channel = bootstrap.bind(8081).sync();
// channels.add(bootstrap.bind(8080).sync());
} catch (final InterruptedException e) {}
}
public void shutdown() {
masterGroup.shutdownGracefully();
try {
channel.channel().closeFuture().sync();
} catch (InterruptedException e) {}
}
private void registerToPubSub(final ChannelHandlerContext ctx, Object msg) {
new Thread() {
#Override
public void run() {
while (true) {
final String responseMessage = "data:abcdef\n\n";
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
copiedBuffer(responseMessage.getBytes()));
response.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.KEEP_ALIVE);
response.headers().set(HttpHeaders.Names.CONTENT_TYPE, "text/event-stream");
response.headers().set(HttpHeaders.Names.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
response.headers().set("Cache-Control", "no-cache");
ctx.writeAndFlush(response);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}.start();
}
public static void main(String[] args) {
new NettyHttpServer().start();
}
}
Client js code (I run it more than 5 times from my browser in different tabs, and the not all of them get:
var source = new EventSource("http://localhost:8081");
source.onmessage = function(event) {
console.log(event.data);
};
source.onerror= function(err){console.log(err); source.close()};
source.onopen = function(event){console.log('open'); console.log(event)}
You need to let the browser know that you are done sending the response, and for that you have three options.
Set a content length
Send it chunked
Close the connection when you are done
You aren't doing any of those. I suspect your browser is still waiting for the full response to each request you send, and is using a new connection for each request in your testing. After 5 requests your browser must be refusing to create new connections.
Another thing I noticed is that you are creating a new thread for each request in your server, and never letting it die. That will cause problems down the line as you try to scale. If you really want that code to run in a different thread then I suggest looking at overloaded methods for adding handlers to the pipeline; those should let you specify a thread pool to run them in.

SMS Receiving using JSMPP sometimes stop

I am using JSMPP from Google to receive SMS messages from SMS Service Centre. Sometimes, my program stops receiving SMS from SMSC, I have to close the program and Re-Open the program. Then Queued SMSes from SMSC starts receive. This happens after some time, like after 7 or 8 hours. Here is the code I'v used
SMPP Init Code
l.info("SMPP Initialization");
SMPPSession s = new SMPPSession();
s.setMessageReceiverListener(new Receive(s));
s.connectAndBind(Settings.smsc_host,Settings.smsc_port, BindType.BIND_TRX,
Settings.smsc_user, Settings.smsc_password,Settings.smsc_msg_setting, TypeOfNumber.UNKNOWN,
NumberingPlanIndicator.UNKNOWN, null, Settings.smsc_timeout);
ProcessSMS.s = s;
l.info("SMPP Initialization Success");
SMS Receive Code
package sms;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.log4j.Logger;
import org.jsmpp.bean.AlertNotification;
import org.jsmpp.bean.DataSm;
import org.jsmpp.bean.DeliverSm;
import org.jsmpp.bean.MessageType;
import org.jsmpp.extra.ProcessRequestException;
import org.jsmpp.session.DataSmResult;
import org.jsmpp.session.MessageReceiverListener;
import org.jsmpp.session.Session;
import processor.ProcessSMS;
public class Receive implements MessageReceiverListener {
private static final ExecutorService pool = Executors.newFixedThreadPool(10);
private static final Logger l = Logger.getLogger(Receive.class);
private Thread thread;
public Receive(){
super();
}
public Receive(Session s){
this();
}
#Override
public DataSmResult onAcceptDataSm(DataSm arg0, Session arg1)
throws ProcessRequestException {
return null;
}
#Override
public void onAcceptAlertNotification(AlertNotification arg0) {
}
#Override
public void onAcceptDeliverSm(DeliverSm arg0)
throws ProcessRequestException {
l.info("Received SMS " + arg0);
if(MessageType.SMSC_DEL_RECEIPT.containedIn(arg0.getEsmClass())){
}else{
pool.submit(new ProcessSMS(arg0));
//thread = new Thread(new ProcessSMS(arg0));
//thread.start();
}
}
}
And here is the state change class
package sms;
import java.io.IOException;
import global.Shared;
import log.SMSCStateLogger;
import org.jsmpp.bean.BindType;
import org.jsmpp.bean.NumberingPlanIndicator;
import org.jsmpp.bean.TypeOfNumber;
import org.jsmpp.extra.SessionState;
import org.jsmpp.session.SMPPSession;
import org.jsmpp.session.Session;
import org.jsmpp.session.SessionStateListener;
import processor.ProcessSMS;
import settings.Settings;
public class StateChange implements SessionStateListener{
private static SMSCStateLogger l = new SMSCStateLogger(StateChange.class);
private Session s;
#Override
public void onStateChange(SessionState arg0, SessionState arg1, Object arg2) {
//arg0 = new State
//arg1 = old State
if(!arg0.isBound() && arg1.isBound()){
int con = Shared.getNextReConnectInterval();
l.info("State changed from " + arg1 + " to " + arg0 + " on " + arg2);
while(true){
l.info("Re Connection in " + con + " ms");
try{
Thread.sleep(con);
}catch(InterruptedException iex){
l.fatal("Re Connection failed due to exception " + iex);
break;
}
s = new SMPPSession();
((SMPPSession) s).setMessageReceiverListener(new Receive(s));
s.addSessionStateListener(new StateChange());
try{
((SMPPSession) s).connectAndBind(Settings.smsc_host,Settings.smsc_port, BindType.BIND_TRX,
Settings.smsc_user, Settings.smsc_password,Settings.smsc_msg_setting, TypeOfNumber.UNKNOWN,
NumberingPlanIndicator.UNKNOWN, null, Settings.smsc_timeout);
}catch(IOException ioex){
l.fatal("Connection failed due to " + ioex.getMessage());
}
ProcessSMS.s = (SMPPSession) s;
l.info("Re Connection success");
break;
}
}
}
}
Does anybody have an idea what is going on ?
Updated the JSMPP API from https://github.com/uudashr/jsmpp.
Download the source with git
run mvn install to create the jar archive.

Categories