Java inheritance with Redis - java

Having subscriber class:
import org.apache.log4j.LogManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.*;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
public class Subscriber extends JedisPubSub {
private static final org.apache.log4j.Logger logger = LogManager.getLogger(Subscriber.class);
#Override
public void onMessage(String channel, String message) {
logger.info("Message received. Channel: " + channel + ", Msg: " + message);
}
#Override
public void onSubscribe(String channel, int subscribedChannels) {
logger.info("Subscribed to channel: " + channel);
}
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinelsHashSet, password);
Jedis jPublisher = pool.getResource();
Jedis jedis = pool.getResource();
Subscriber subscriber = new Subscriber();
jedis.subscribe(subscriber, channel);
jedis.quit();
} catch (Exception e) {
logger.error(e.toString());
}
}
});
t.run();
}
}
Which basically print all messages received on Redis channel I wanted to create child class with different onMessage or onSubscribe methods. Im calling Subscriber class from Main class by
Subscriber sb = new Subscriber();
sb.main(new String[]{});
So I have tried:
Copy main method and change Subscriber subscriber = new Subscriber(); to SubscriberExtended subscriber = new SubscriberExtended(); and call from Main class by:
1.1)
Subscriber sb = new SubscriberExtended();
sb.main(new String[]{});
or
SubscriberExtended sb = new SubscriberExtended();
sb.main(new String[]{});
import org.apache.log4j.LogManager;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisSentinelPool;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Properties;
public class SubscriberExtended extends Subscriber {
private static final org.apache.log4j.Logger logger = LogManager.getLogger(SubscriberExtended.class);
#Override
public void onSubscribe(String channel, int subscribedChannels) {
logger.info("Subscribed to channel from Extended class: " + channel);
}
public SubscriberExtended() {
}
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinelsHashSet, password);
Jedis jPublisher = pool.getResource();
Jedis jedis = pool.getResource();
SubscriberExtended subscriber = new SubscriberExtended();
jedis.subscribe(subscriber, channel);
jedis.quit();
} catch (Exception e) {
logger.error(e.toString());
}
}
});
t.run();
}
Also I have tried to put in constructor
public SubscriberExtended() {
super.main(new String[]{});
}
And few others configuration of those and nothing seems to be working.
What I'm trying to achieve is to create SubscriberExtended class which will behave same as Subscriber class but override onMessage or onSubscribe methods. Can anyone help me?

This appears that it should work, although I am not sure why you are adding a static main to all the classes.
You should be able to do the following:
public class Subscriber extends JedisPubSub {
private static final org.apache.log4j.Logger logger = LogManager.getLogger(Subscriber.class);
#Override
public void onMessage(String channel, String message) {
logger.info("Message received. Channel: "+channel+", Msg: "+message);
}
#Override
public void onSubscribe(String channel, int subscribedChannels) {
logger.info("Subscribed to channel: " + channel);
}
public void start() {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
try {
JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinelsHashSet, password);
Jedis jedis = pool.getResource();
jedis.subscribe(this, channel);
jedis.quit();
} catch (Exception e) {
logger.error(e.toString());
}
}
});
t.start();
}
}
public class SubscriberExtended extends Subscriber {
#Override
public void onMessage(String channel, String message) {
logger.info("Extended Message received. Channel: "+channel+", Msg: "+message);
}
}
Then, from your main function somewhere, you would have the following:
public class Main()
{
public static void main(String[] args)
{
SubscriberExtended se = new SubscriberExtended();
se.start();
while(true) {
// do something else or sleep
}
}
}
I believe one mistake you made was to call t.run() instead of t.start(). t.run() will not return, unless the subscribe fails for some reason such as the connection to REDIS was closed. t.start() will kick off the thread.
You also seemed to grab a publisher connection from the jedis pool for no reason.
Another problem is here:
Subscriber sb = new SubscriberExtended();
sb.main(new String[]{});
sb.main will proceed to also call new SubscriberExtended() to use with the subscribe, and so your sb object will not receive any publications - they will go to the instance created inside sb.main instead. Using 'this' inside the start() method to subscribe will address that issue.
Once that is set up, you can go ahead and connect to REDIS with redis-cli and issue a publish to see if your program receives the message.

Related

BlockingQueue loses its reference and throws NullPointerException onMessage Jetty WebSocket

Had to ask this question because it's been a day trying to solve the problem and cannot.
I'm working with Netbeans 8.2 and java 8.
Topology:
WebSocket client on the browser
Jetty WebSocket server (java app with swing GUI)
Objective: to send data from client to server and show the data on JTextArea (GUI)
Main.java (GUI)
public class Main extends javax.swing.JPanel {
private WebSocketSwing websocketserver;
private BlockingQueue<String> stack = new ArrayBlockingQueue<String>(3);
public Main() {
initComponents();
// WebSocketServer
websocketserver = new WebSocketSwing(stack);
websocketserver.start();
consumer.start();
}
Thread consumer = new Thread(new Runnable() {
#Override
public void run() {
try{
String msg;
//consuming messages until exit message is received
while((msg = stack.take()) !="exit"){
Thread.sleep(10);
System.out.println("Consumed: " + msg);
}
}catch(InterruptedException e) {
e.printStackTrace();
}
}
});
private void initComponents() {
//GUI code goes here
}
public static void main(String[] args) {
JFrame frame = new JFrame("Main GUI");
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
frame.setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new SDG());
frame.pack();
frame.setVisible(true);
}
});
}
private javax.swing.JLabel jLabel1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextArea txt_area;
}
WebSocketSwing class
public class WebSocketSwing extends Thread {
/**
* #param args the command line arguments
*/
private BlockingQueue<String> stack;
public WebSocketSwing(BlockingQueue<String> queue){
this.stack = queue;
}
#Override
public void run(){
super.run();
try {
Server server = new Server(2014);
WSHandler mHandler = new WSHandler();
mHandler.SetStack(stack);
server.setHandler(mHandler);
server.setStopTimeout(0);
server.start();
//
server.join();
} catch (Exception e) {
e.printStackTrace();
}
}
}
WSHandler class
#WebSocket
public class WSHandler extends WebSocketHandler {
private Session session;
public BlockingQueue<String> stack = new ArrayBlockingQueue<String>(3); // **THIS instantiation should not be needed...**
private static ArrayList<WSHandler> sessions = new ArrayList<WSHandler>();
public static ArrayList<WSHandler> getAllSessions() {
return sessions;
}
/*WSHandler(BlockingQueue<String> stack) { // **I tried to send/assign the queue from the constructor but the method is not overridable**
SetStack(stack); // or this.stack = stack;
}*/
public void SetStack(BlockingQueue<String> queue){
this.stack = queue;
//Testing operations to see the reference to the queue was successfully passed
System.out.println(stack.remainingCapacity());
stack.offer("Something"); //**consumes just fine in the other Thread...**
}
#OnWebSocketClose
public void onClose(int StatusCode, String reason){
sessions.remove(this);
System.out.println("Close: Status Code: " + StatusCode + ", reason: " + reason + ", sessions = " + sessions.size());
}
#OnWebSocketError
public void onError(Throwable t) {
System.out.println("Error: " + t.getMessage());
}
#OnWebSocketConnect
public void onConnect(Session localSession) {
session = localSession;
sessions.add(this);
System.out.println("Connect: " + session.getRemoteAddress().getAddress());
}
#OnWebSocketMessage
public void onMessage(String message) {
try {
System.out.println("Message: " + message);
session.getRemote().sendString("ACK");
SetData(message);
if(message.equals("exit")){
System.out.println("Message: Bye!...");
System.exit(0);
}
} catch (IOException ex) {
Logger.getLogger(WSHandler.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void SetData(String message){
try{
if (stack.offer(message)){
System.out.print("Inserted");
} else {
System.out.print("NOT Inserted");
}
} catch(NullPointerException e){
e.printStackTrace();
}
}
#Override
public void configure(WebSocketServletFactory factory) {
factory.register(WSHandler.class);
}
}
Result> seems like this.stack lost its reference to the queue...
as if I do not initialize the BlockingQueue inside the class throws the NPE outside the SetStack Method...
Trace (when I do not initialize the BlockingQueue on WSHandler class)
Situation if my understanding is correct if the reference from the Main class has been passed correctly I should not need to initialize the BlockingQueue in the Handler... Then I think that's the issue to be solved...
The NullPointerException is thrown because the object lost its reference (the one it had in the SetStack method...)... that reason is what I haven't been able to find...
2021-06-26 15:35:41.990:INFO::Thread-2: Logging initialized #470ms to org.eclipse.jetty.util.log.StdErrLog
3
2021-06-26 15:35:42.077:INFO:oejs.Server:Thread-2: jetty-9.4.42.v20210604; built: 2021-06-04T17:33:38.939Z; git: 5cd5e6d2375eeab146813b0de9f19eda6ab6e6cb; jvm 1.8.0_111-b14
Consumed: Something
2021-06-26 15:35:42.827:INFO:oejs.AbstractConnector:Thread-2: Started ServerConnector#98f7e6f{HTTP/1.1, (http/1.1)}{0.0.0.0:2014}
2021-06-26 15:35:42.830:INFO:oejs.Server:Thread-2: Started #1314ms
Connect: /127.0.0.1
Message: sample message
Happened
java.lang.NullPointerException
at websocketswing.WSHandler.SetData(WSHandler.java:100)
at websocketswing.WSHandler.onMessage(WSHandler.java:78)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.eclipse.jetty.websocket.common.events.annotated.CallableMethod.call(CallableMethod.java:70)
at org.eclipse.jetty.websocket.common.events.annotated.OptionalSessionCallableMethod.call(OptionalSessionCallableMethod.java:72)
at org.eclipse.jetty.websocket.common.events.JettyAnnotatedEventDriver.onTextMessage(JettyAnnotatedEventDriver.java:301)
at org.eclipse.jetty.websocket.common.message.SimpleTextMessage.messageComplete(SimpleTextMessage.java:69)
at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.appendMessage(AbstractEventDriver.java:67)
at org.eclipse.jetty.websocket.common.events.JettyAnnotatedEventDriver.onTextFrame(JettyAnnotatedEventDriver.java:287)
at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.incomingFrame(AbstractEventDriver.java:152)
at org.eclipse.jetty.websocket.common.WebSocketSession.incomingFrame(WebSocketSession.java:326)
at org.eclipse.jetty.websocket.common.extensions.AbstractExtension.nextIncomingFrame(AbstractExtension.java:148)
at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.nextIncomingFrame(PerMessageDeflateExtension.java:111)
at org.eclipse.jetty.websocket.common.extensions.compress.CompressExtension.forwardIncoming(CompressExtension.java:169)
at org.eclipse.jetty.websocket.common.extensions.compress.PerMessageDeflateExtension.incomingFrame(PerMessageDeflateExtension.java:90)
at org.eclipse.jetty.websocket.common.extensions.ExtensionStack.incomingFrame(ExtensionStack.java:202)
at org.eclipse.jetty.websocket.common.Parser.notifyFrame(Parser.java:225)
at org.eclipse.jetty.websocket.common.Parser.parseSingleFrame(Parser.java:259)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:459)
at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:440)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105)
at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173)
at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.produce(EatWhatYouKill.java:137)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:882)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1036)
at java.lang.Thread.run(Thread.java:745)
Trace (when initializing the Queue)
2021-06-26 15:39:36.821:INFO::Thread-2: Logging initialized #470ms to org.eclipse.jetty.util.log.StdErrLog
3
2021-06-26 15:39:36.889:INFO:oejs.Server:Thread-2: jetty-9.4.42.v20210604; built: 2021-06-04T17:33:38.939Z; git: 5cd5e6d2375eeab146813b0de9f19eda6ab6e6cb; jvm 1.8.0_111-b14
Consumed: Something
2021-06-26 15:39:37.961:INFO:oejs.AbstractConnector:Thread-2: Started ServerConnector#358d4f07{HTTP/1.1, (http/1.1)}{0.0.0.0:2014}
2021-06-26 15:39:37.964:INFO:oejs.Server:Thread-2: Started #1615ms
Connect: /127.0.0.1
Message: sample message
Happened
Inserted
Message: sample message
Happened
NOT Inserted
Message: sample message
Happened
NOT Inserted
Hence, I've assumed the queue loses its reference because "this queue" never gets consumed by the Consumer thread (as it does in the first assignment)
Hope someone can see what I'm not seeing...
Best regards,
A new WSHandler instance gets created with each new (and accepted/upgraded) WebSocket connection because of how you registered it in your configure() method ...
#Override
public void configure(WebSocketServletFactory factory) {
factory.register(WSHandler.class);
}
Restructure your code.
Start by separating the WSHandler from the WebSocket Endpoint.
Make the new MyEndpoint have a constructor (or setter) for your queue object.
#WebSocket
public class MyEndpoint {
private .... queue;
public MyEndpoint(... queue) {
this.queue = queue;
}
#OnWebSocketMessage
public void onMessage(String str) {
this.queue.offer(str);
}
}
Next, you want to create a custom org.eclipse.jetty.websocket.servlet.WebSocketCreator of your own design that creates the WebSocket endpoint instance, populates it, and then hands it back to the Jetty implementation.
public static class MyWebSocketCreator implements WebSocketCreator {
private ... masterQueue = new ...;
#Override
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp) {
return new MyEndpoint(masterQueue);
}
}
Lastly, you want to make your configure() method use this new creator.
#Override
public void configure(WebSocketServletFactory factory) {
factory.setCreator(new MyWebSocketCreator());
}
This is covered in my prior answer, option 2, at How do I access instantiated WebSockets in Jetty 9?

Why do I see performance drops on Netty server bootstrap accepting new channels?

I am still using Netty 3.10. I wrote an unit test to check on performance of Netty boss thread. I use a simple Netty server bootstrap within the unit test main thread and spawn 100 Java sync-IO clients within a cached thread pool. I noticed performance drops that I think are strange. Every client opens a socket, writes data and closes, logging duration (ms) after close. My unit test is attached. Typical output of my unit test is, in given order:
43 x Client done. Duration: 0
26 x Client done. Duration: 16
16 x Client done. Duration: 0
3 x Client done. Duration: 517
11 x Client done. Duration: 3003
1 x Client done. Duration: 6036
So there was 1 client that had to wait for 6 seconds to get an open TCP/IP channel and 11 clients that had to wait for 3 seconds. I also checked where the time is spent/lost. It is always new Socket(String,int) at the client side. At server side the time is already gone when pipeline factory gets triggered.
Is the threading model of my unit test the cause of this or really Netty bootstrap/boss?
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFactory;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.channel.ChannelPipelineFactory;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.group.ChannelGroup;
import org.jboss.netty.channel.group.DefaultChannelGroup;
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
public class NettyServerBossTest {
private static final String SRV_HOST = "localhost";
private static final int SRV_PORT = 8080;
private static final byte[] MSG = "Hello world!".getBytes(Charset.forName("UTF-8"));
private static final int WAIT_MAX_MILLIS = 10 * 1000;
private final ChannelGroup channels = new DefaultChannelGroup();
private final int expected = 100;
private final AtomicInteger actual = new AtomicInteger();
private volatile boolean failed;
private ExecutorService clientThreads;
private Throwable cause;
private ServerBootstrap bootstrap;
#Test
public void test() {
createServer();
createClients();
awaitClients();
verifyFailure();
}
private void awaitClients() {
final long startMillis = System.currentTimeMillis();
final long maxMillis = startMillis + WAIT_MAX_MILLIS;
while ((this.actual.get() < this.expected) && !isFailed() && (System.currentTimeMillis() < maxMillis)) {
try {
Thread.sleep(250L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Total duration: " + (System.currentTimeMillis() - startMillis));
Assert.assertEquals(this.expected, this.actual.get());
}
private void createClients() {
this.clientThreads = Executors.newCachedThreadPool();
for (int i = 0; i < this.expected; i++) {
this.clientThreads.execute(new PlainSocketClient());
}
}
private void closeChannels() {
try {
this.channels.close().await(10000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void createServer() {
final ExecutorService bosses = Executors.newCachedThreadPool();
final ExecutorService workers = Executors.newCachedThreadPool();
final ChannelFactory factory = new NioServerSocketChannelFactory(bosses, workers);
this.bootstrap = new ServerBootstrap(factory);
this.bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
#Override
public ChannelPipeline getPipeline() {
return Channels.pipeline(new DiscardServerHandler());
}
});
this.bootstrap.setOption("child.tcpNoDelay", Boolean.TRUE);
this.bootstrap.setOption("child.keepAlive", Boolean.TRUE);
this.bootstrap.bind(new InetSocketAddress(SRV_HOST, SRV_PORT));
}
/**
* Fail unit test
*
* #param cause
* cause of failure
*/
public synchronized void setCause(Throwable cause) {
if (!this.failed && (cause == null)) {
this.failed = true;
this.cause = cause;
}
}
#After
public void after() {
closeChannels();
if (this.clientThreads != null) {
this.clientThreads.shutdownNow();
}
if (this.bootstrap != null) {
this.bootstrap.releaseExternalResources();
}
}
/**
* Check if unit test has failed
*
* #return <code>true</code> if failed, <code>false</code> if still OK
*/
public boolean isFailed() {
return this.failed;
}
/**
* Get cause of failure
*
* #return cause or <code>null</code>
*/
public synchronized Throwable getCause() {
return this.cause;
}
/**
* Make sure test has not failed with exception
*/
public void verifyFailure() {
if (this.failed) {
throw new IllegalStateException("test failed", getCause());
}
}
public abstract class TestRunnable implements Runnable {
#Override
public final void run() {
try {
execute();
} catch (Exception e) {
handleException(e);
}
}
protected abstract void handleException(Throwable e);
protected abstract void execute() throws Exception;
}
public abstract class AsyncThreadsTestRunnable extends TestRunnable {
#Override
protected final void handleException(Throwable e) {
setCause(e);
}
}
public class PlainSocketClient extends AsyncThreadsTestRunnable {
#Override
protected void execute() throws Exception {
final long startMillis = System.currentTimeMillis();
try (Socket sock = new Socket(SRV_HOST, SRV_PORT)) {
sock.getOutputStream().write(MSG);
}
NettyServerBossTest.this.actual.incrementAndGet();
System.out.println("Client done. Duration: " + (System.currentTimeMillis() - startMillis));
}
}
public class DiscardServerHandler extends SimpleChannelHandler {
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) {
NettyServerBossTest.this.channels.add(e.getChannel());
}
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
// discard
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
e.getCause().printStackTrace();
Channel ch = e.getChannel();
ch.close();
}
}
}
I think the time you logged does not all spend by open socket, it spend by thread switching because when Thread A open a socket then CPU may switching to Thread B, then when socket open is done, CPU maybe does not switch to Thread A at once but after executing many other threads. I have change your PlainSocketClient to add an synchronized to make sure there are less thread switching affect:
public class PlainSocketClient extends AsyncThreadsTestRunnable {
private static final String LOCK = "LOCK";
#Override
protected void execute() throws Exception {
synchronized (LOCK) {
final long startMillis = System.currentTimeMillis();
try (Socket sock = new Socket(SRV_HOST, SRV_PORT)) {
sock.getOutputStream().write(MSG);
}
NettyServerBossTest.this.actual.incrementAndGet();
System.out.println("Client done. Duration: " + (System.currentTimeMillis() - startMillis));
}
}
}
then they almost only output 0 or 1. You can have a testing yourself.
It just proof the time consuming at thread switching, not mean you need to add an synchronized in your code.

Java Eclipse Paho Implementation - Auto reconnect

I'm trying to implement eclipse.paho in my project to connect Mqtt Broker (Both subscribing and publishing purpose). The problem is, when I using the subscribing feature (Implementing MqttCallback interface), I couldn't figure our how can I reconnect if the connection lost. MqttCallback interface has a connectionLost method, but it is useful for the debug what causes the connection lost. I searched but couldn't find a way to establish auto reconnect. Can you suggest a way or document about this problem?
I'm using the paho client 1.2.0.
With the MqttClient.setAutomaticReconnect(true) and interface MqttCallbackExtended API, and thanks to https://github.com/eclipse/paho.mqtt.java/issues/493, I could manage to reconnect automatically when the connection to broker is down.
See below the code.
//Use the MqttCallbackExtended to (re-)subscribe when method connectComplete is invoked
public class MyMqttClient implements MqttCallbackExtended {
private static final Logger logger = LoggerFactory.getLogger(MqttClientTerni.class);
private final int qos = 0;
private String topic = "mytopic";
private MqttClient client;
public MyMqttClient() throws MqttException {
String host = "tcp://localhost:1883";
String clientId = "MQTT-Client";
MqttConnectOptions conOpt = new MqttConnectOptions();
conOpt.setCleanSession(true);
//Pay attention here to automatic reconnect
conOpt.setAutomaticReconnect(true);
this.client = new org.eclipse.paho.client.mqttv3.MqttClient(host, clientId);
this.client.setCallback(this);
this.client.connect(conOpt);
}
/**
* #see MqttCallback#connectionLost(Throwable)
*/
public void connectionLost(Throwable cause) {
logger.error("Connection lost because: " + cause);
/**
* #see MqttCallback#deliveryComplete(IMqttDeliveryToken)
*/
public void deliveryComplete(IMqttDeliveryToken token) {
}
/**
* #see MqttCallback#messageArrived(String, MqttMessage)
*/
public void messageArrived(String topic, MqttMessage message) throws MqttException {
logger.info(String.format("[%s] %s", topic, new String(message.getPayload())));
}
public static void main(String[] args) throws MqttException, URISyntaxException {
MyMqttClient s = new MyMqttClient();
}
#Override
public void connectComplete(boolean arg0, String arg1) {
try {
//Very important to resubcribe to the topic after the connection was (re-)estabslished.
//Otherwise you are reconnected but you don't get any message
this.client.subscribe(this.topic, qos);
} catch (MqttException e) {
e.printStackTrace();
}
}
}
The best way to do this is to structure your connection logic so it lives in a method on it's own so it can be called again from the connectionLost callback in the MqttCallback instance.
The connectionLost method is passed a Throwable that will be the exception that triggered the disconnect so you can make decisions about the root cause and how this may effect when/how you reconnect.
The connection method should connect and subscribe to the topics you require.
Something like this:
public class PubSub {
MqttClient client;
String topics[] = ["foo/#", "bar"];
MqttCallback callback = new MqttCallback() {
public void connectionLost(Throwable t) {
this.connect();
}
public void messageArrived(String topic, MqttMessage message) throws Exception {
System.out.println("topic - " + topic + ": " + new String(message.getPayload()));
}
public void deliveryComplete(IMqttDeliveryToken token) {
}
};
public static void main(String args[]) {
PubSub foo = new PubSub();
}
public PubSub(){
this.connect();
}
public void connect(){
client = new MqttClient("mqtt://localhost", "pubsub-1");
client.setCallback(callback);
client.connect();
client.subscribe(topics);
}
}
To use auto reconnect, just set setAutomaticReconnect(true) on the MqttConnectOptions object.
MqttAndroidClient mqttClient = new MqttAndroidClient(context, mqttUrl, clientId);
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setAutomaticReconnect(true);
mqttClient.connect(mqttConnectOptions, null, mqttActionListener());

Why do asynchronous channels in Java's NIO.2 require these pauses?

I've written minimal example, but it's still too long, so let me know I should post the link to Pastebin instead.
Server:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class Srv {
static final int PORT = 9001;
static final String HOST = "127.0.0.1";
public static void runInstance() {
try (AsynchronousServerSocketChannel asynchronousServerSocketChannel =
AsynchronousServerSocketChannel.open()) {
if (asynchronousServerSocketChannel.isOpen()) {
asynchronousServerSocketChannel.setOption(StandardSocketOptions.SO_RCVBUF, 1024);
asynchronousServerSocketChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
asynchronousServerSocketChannel.bind(new InetSocketAddress(HOST, PORT));
System.out.println(String.format("Launched master on %s:%d", HOST, PORT));
asynchronousServerSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
#Override
public void completed(AsynchronousSocketChannel result, Void attachment) {
asynchronousServerSocketChannel.accept(null, this);
try {
result.read(ByteBuffer.allocate(1024));
System.out.println("Conn from:" + result.getRemoteAddress());
} catch (Exception exn) {
exn.printStackTrace();
} finally {
try {
result.close();
} catch (IOException exn) {
exn.printStackTrace();
}
}
}
#Override
public void failed(Throwable exn, Void attachment) {
asynchronousServerSocketChannel.accept(null, this);
throw new UnsupportedOperationException("can't accept");
}
});
System.in.read();
} else {
System.out.println("The asynchronous server-socket channel cannot be opened");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import static java.lang.Thread.sleep;
public class Wrk {
static final int PORT = 9001;
static final String HOST = "127.0.0.1";
public static void runInstance() throws InterruptedException {
sleep(1000); //HERE
try(AsynchronousSocketChannel asynchronousSocketChannel = AsynchronousSocketChannel.open()) {
if (asynchronousSocketChannel.isOpen()) {
asynchronousSocketChannel.setOption(StandardSocketOptions.SO_RCVBUF, 1024);
asynchronousSocketChannel.setOption(StandardSocketOptions.SO_SNDBUF, 1024);
asynchronousSocketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
asynchronousSocketChannel.connect(new InetSocketAddress(HOST, PORT), null,
new CompletionHandler<Void, Void>() {
#Override
public void completed(Void result, Void attachment) {
try {
System.out.println("Connected to: " + HOST + PORT);
asynchronousSocketChannel.read(ByteBuffer.allocate(1024)).get();
} catch (Exception exn) {
exn.printStackTrace();
} finally {
try {
asynchronousSocketChannel.close();
} catch (IOException exn) {
exn.printStackTrace();
}
}
}
#Override
public void failed(Throwable throwable, Void o) {
System.out.println("Connection cannot be established");
}
});
sleep(1000); //AND HERE
} else {
System.out.println("The asynchronous socket channel cannot be opened");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Boilerplate to run:
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread srv = new Thread(new Runnable() {
#Override
public void run() {
Srv.runInstance();
}
});
srv.start();
Thread wrk1 = new Thread(new Runnable() {
#Override
public void run() {
try {
Wrk.runInstance();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
wrk1.start();
}
}
So, if I have the code like this, it gives me the following output:
Launched master on 127.0.0.1:9001
Connected to: 127.0.0.1 9001
Conn from:/127.0.0.1:50415
But if I remove those sleep()s in Srv.runInstance(), I get:
Connection cannot be established
Launched master on 127.0.0.1:9001
Conn from:/127.0.0.1:50438
So, does that mean that client connects to server, but server refuses? I don't clearly understand what happens here and documentation is rather poor, so I don't know where to look for solutions.
The only sleep I see in the posted code is in Wrk.runInstance. However, the order of your output makes it pretty clear what is going on. Your working is trying to connect to the server before it has fully initialized. As you can see in your output, the "Connection message" is before the "Launched master".
The server takes a bit of time to start, so without the sleep, your client is trying to connect to something which is not yet there.

Kryo - How to keep the server running and waiting for requests

I use Kryo-net for send and receive messages. On the server side I open new thread and sets the server, the problem is that the thread is ended at the end of the code so there isnt really listener for requests.
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.kryonet.Server;
public class MessagingServer implements Runnable{
private Server server;
public void stop(){
this.server.stop();
}
public MessagingServer(){
this.server = new Server();
}
#Override
public void run() {
try{
if(server!=null){ stop(); }
this.server.start();
this.server.bind(54555, 54777);
Kryo kryo = this.server.getKryo();
kryo.register(NewRequiredJobRequest.class);
kryo.register(NewRequiredJobResponse.class);
server.addListener(new Listener() {
#Override
public void received (Connection connection, Object object) {
if (object instanceof HelloRequest) {
HelloRequest request = (HelloRequest)object;
System.out.println(request.text);
HelloResponse response = new HelloResponse();
response.text = "Thanks!";
connection.sendTCP(response);
}
}
});
} catch (Exception e) {
System.out.println("kryo server exception"));
}
// once the code reach here the thread is ended..
}
}
Server object probably has some sort of listen or accept method that must run in a loop.
KryoNet Client#start and Server#start starts a daemon thread. If you have no other non-daemon threads in your app:
new Thread(client).start();
new Thread(server).start();
I've never use kryo, but I think this will help.
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.kryonet.Server;
public class MessagingServer implements Runnable{
private Server server;
public void stop(){
this.server.stop();
}
public MessagingServer(){
this.server = new Server();
}
#Override
public void run() {
try{
if(server!=null){ stop(); }
this.server.start();
this.server.bind(54555, 54777);
while(true) {
Kryo kryo = this.server.getKryo();
kryo.register(NewRequiredJobRequest.class);
kryo.register(NewRequiredJobResponse.class);
server.addListener(new Listener() {
#Override
public void received (Connection connection, Object object) {
if (object instanceof HelloRequest) {
HelloRequest request = (HelloRequest)object;
System.out.println(request.text);
HelloResponse response = new HelloResponse();
response.text = "Thanks!";
connection.sendTCP(response);
}
}
});
}
} catch (Exception e) {
System.out.println("kryo server exception"));
}
}
}

Categories