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?
Related
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.
SparkJava web sockets won't work. Whenever I attempt to connect to it with a websocket tester, at 'ws://localhost:4567/echo' it gets an error 'undefined' and never connects, nor do any of the sout's or printStackTrace get called.
#WebSocket
public class EchoWebSocket {
private static final Queue<Session> sessions = new ConcurrentLinkedQueue<>();
#OnWebSocketConnect
public void connected(Session session) {
System.out.println("Client connected");
//sessions.add(session);
}
#OnWebSocketClose
public void closed(Session session, int statusCode, String reason) {
System.out.println("Client disconnected");
//sessions.remove(session);
}
#OnWebSocketMessage
public void message(Session session, String message) throws IOException {
System.out.println("Got: ");// + message); // Print message
//session.getRemote().sendString(message); // and send it back
}
#OnWebSocketError
public void throwError(Throwable error) {
error.printStackTrace();
}
}
how I call it
Spark.webSocket("/echo", new EchoWebSocket());
Spark.init();
You need to define the class, not create an object.
Spark.webSocket("/echo", EchoWebSocket.class);
I am reading "Netty In Action V5". When reading to chapter 2.3 and 2.4, I tried with example EchoServer and EchoClient, when I tested one client connected to server, everything worked perfectly ... then I modified the example to multi clients could connect to server. My purpose was to run a stresstest : 1000 clients would connect to server, and each of client would echo 100 messages to server, and when all clients finished, I would get total time of all of process. Server was deployed on linux machine (VPS), and clients were deployed on window machine.
When run stresstest, I got 2 problems:
Some clients got error message:
java.io.IOException: An existing connection was forcibly closed by the remote host
at sun.nio.ch.SocketDispatcher.read0(Native Method)
at sun.nio.ch.SocketDispatcher.read(SocketDispatcher.java:43)
at sun.nio.ch.IOUtil.readIntoNativeBuffer(IOUtil.java:223)
at sun.nio.ch.IOUtil.read(IOUtil.java:192)
at sun.nio.ch.SocketChannelImpl.read(SocketChannelImpl.java:379)
at io.netty.buffer.UnpooledUnsafeDirectByteBuf.setBytes(UnpooledUnsafeDirectByteBuf.java:447)
at io.netty.buffer.AbstractByteBuf.writeBytes(AbstractByteBuf.java:881)
at io.netty.channel.socket.nio.NioSocketChannel.doReadBytes(NioSocketChannel.java:242)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:119)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:511)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:468)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:382)\at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:354)
at io.netty.util.concurrent.SingleThreadEventExecutor$2.run(SingleThreadEventExecutor.java:110)
at io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:137)
at java.lang.Thread.run(Thread.java:745)
But some clients did not received message from server
Working Enviroment:
Netty-all-4.0.30.Final
JDK1.8.0_25
Echo Clients were deployed on Window 7 Ultimate
Echo Server was deployed on Linux Centos 6
Class NettyClient:
public class NettyClient {
private Bootstrap bootstrap;
private EventLoopGroup group;
public NettyClient(final ChannelInboundHandlerAdapter handler) {
group = new NioEventLoopGroup();
bootstrap = new Bootstrap();
bootstrap.group(group);
bootstrap.channel(NioSocketChannel.class);
bootstrap.handler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline().addLast(handler);
}
});
}
public void start(String host, int port) throws Exception {
bootstrap.remoteAddress(new InetSocketAddress(host, port));
bootstrap.connect();
}
public void stop() {
try {
group.shutdownGracefully().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Class NettyServer:
public class NettyServer {
private EventLoopGroup parentGroup;
private EventLoopGroup childGroup;
private ServerBootstrap boopstrap;
public NettyServer(final ChannelInboundHandlerAdapter handler) {
parentGroup = new NioEventLoopGroup(300);
childGroup = new NioEventLoopGroup(300);
boopstrap = new ServerBootstrap();
boopstrap.group(parentGroup, childGroup);
boopstrap.channel(NioServerSocketChannel.class);
boopstrap.childHandler(new ChannelInitializer<SocketChannel>() {
#Override
protected void initChannel(SocketChannel channel) throws Exception {
channel.pipeline().addLast(handler);
}
});
}
public void start(int port) throws Exception {
boopstrap.localAddress(new InetSocketAddress(port));
ChannelFuture future = boopstrap.bind().sync();
System.err.println("Start Netty server on port " + port);
future.channel().closeFuture().sync();
}
public void stop() throws Exception {
parentGroup.shutdownGracefully().sync();
childGroup.shutdownGracefully().sync();
}
}
Class EchoClient
public class EchoClient {
private static final String HOST = "203.12.37.22";
private static final int PORT = 3344;
private static final int NUMBER_CONNECTION = 1000;
private static final int NUMBER_ECHO = 10;
private static CountDownLatch counter = new CountDownLatch(NUMBER_CONNECTION);
public static void main(String[] args) throws Exception {
List<NettyClient> listClients = Collections.synchronizedList(new ArrayList<NettyClient>());
for (int i = 0; i < NUMBER_CONNECTION; i++) {
new Thread(new Runnable() {
#Override
public void run() {
try {
NettyClient client = new NettyClient(new EchoClientHandler(NUMBER_ECHO) {
#Override
protected void onFinishEcho() {
counter.countDown();
System.err.println((NUMBER_CONNECTION - counter.getCount()) + "/" + NUMBER_CONNECTION);
}
});
client.start(HOST, PORT);
listClients.add(client);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}).start();
}
long t1 = System.currentTimeMillis();
counter.await();
long t2 = System.currentTimeMillis();
System.err.println("Totla time: " + (t2 - t1));
for (NettyClient client : listClients) {
client.stop();
}
}
private static class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
private static final String ECHO_MSG = "Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo Echo";
private int numberEcho;
private int curNumberEcho = 0;
public EchoClientHandler(int numberEcho) {
this.numberEcho = numberEcho;
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_MSG, CharsetUtil.UTF_8));
}
#Override
protected void channelRead0(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
curNumberEcho++;
if (curNumberEcho >= numberEcho) {
onFinishEcho();
} else {
ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_MSG, CharsetUtil.UTF_8));
}
}
protected void onFinishEcho() {
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
}
Class EchoServer:
public class EchoServer {
private static final int PORT = 3344;
public static void main(String[] args) throws Exception {
NettyServer server = new NettyServer(new EchoServerHandler());
server.start(PORT);
System.err.println("Start server on port " + PORT);
}
#Sharable
private static class EchoServerHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ctx.write(msg);
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
ctx.flush();
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}
}
You might change 2 things:
Create only one client bootstrap and reuse it for all your clients instead of creating one per client. So extract your bootstrap build out of the Client part and keep only the connect as you've done in your start. This will limit the number of threads internally.
Close the connection on client side when the number of ping pong is reached. Currently you do only a call to the empty method onFinishEcho, which causes no close at all on client side, so there is no client stopping... And therefore no channel closing too...
You might have reach some limitations on the number of threads on client side.
Also one other element could be an issue: you don't specify any codec (string codec or whatever) which could lead to partial sending from client or server treated as full response however.
For instance you might have a first block of "Echo Echo Echo" sending one packet containing the beginning of your buffer, while the other parts (more "Echo") Will be send through later packets.
To prevent this, you should use one codec to ensure your final handler is getting a real full message, not partial one. If not, you might fall in other issues such as error on the server side trying to send extra packet while the channel would be closed by the client sooner as expected...
I have a problem where my class is performing the first run method after which it is not proceeding into a second, overidden run method.
The program execution beings in a controller class which has a main method and a thread pool:
public class RunnableController {
// Main method
public static void main(String[] args) throws InterruptedException {
try {
RunnableController controller = new RunnableController();
controller.initializeDb();
controller.initialiseThreads();
System.out.println("Polling");
} catch (Exception e) {
e.printStackTrace();
}
}
private void initialiseThreads() {
try {
threadExecutorRead = Executors.newFixedThreadPool(10);
PollingSynchronizer read = new PollingSynchronizer(incomingQueue, dbConncetion);
threadExecutorRead.submit(read);
} catch (Exception e){
e.printStackTrace();
}
}
}
My poller class which fetches new data and should do updating simulateously:
public class PollingSynchronizer implements Runnable {
public PollingSynchronizer(Collection<KamMessage> incomingQueue,
Connection dbConnection) {
super();
this.incomingQueue = incomingQueue;
this.dbConnection = dbConnection;
}
private int seqId;
public int getSeqId() {
return seqId;
}
public void setSeqId(int seqId) {
this.seqId = seqId;
}
// The method which runs Polling action and record the time at which it is done
public void run() {
int seqId = 0;
while (true) {
List<KamMessage> list = null;
try {
list = fullPoll(seqId);
if (!list.isEmpty()) {
seqId = list.get(0).getSequence();
incomingQueue.addAll(list);
this.outgoingQueue = incomingQueue;
System.out.println("waiting 3 seconds");
System.out.println("new incoming message");
Thread.sleep(3000);//at this wait I should execute run()
//when I debug my execution stops here and throws " Class not found Exception "
// its does not enters the message processor class
MessageProcessor processor = new MessageProcessor() {
//the run method which should fetch the message processor class.
final public void run() {
MessageProcessor(outgoingQueue).generate(outgoingQueue);
}
};
new Thread(processor).start();
}
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
}
My message processor class:
public abstract class MessageProcessor implements Runnable {
private Connection dbConnection;
Statement st = null;
ResultSet rs = null;
PreparedStatement pstmt = null;
private Collection<KamMessage> outgoingQueue;
public KamMsg804 MessageProcessor(Collection<KamMessage> outgoingQueue,
Connection dbConnection) {
this.outgoingQueue = outgoingQueue;
this.dbConnection = dbConnection;
return (KpiMsg804) fetchedMessages;
}
public Collection<KamMessage> generate(Collection<KamMessage> outgoingQueue) {
while (true) {
try {
while (rs.next()) {
KamMessage filedClass = convertRecordsetToPojo(rs);
outgoingQueue.add(filedClass);
}
for (KamMessage pojoClass : outgoingQueue) {
KamMsg804 updatedValue = createKamMsg804(pojoClass);
System.out.print(" " + pojoClass.getSequence());
System.out.print(" " + pojoClass.getTableName());
System.out.print(" " + pojoClass.getAction());
System.out.print(" " + updatedValue.getKeyInfo1());
System.out.print(" " + updatedValue.getKeyInfo2());
System.out.println(" " + pojoClass.getEntryTime());
}
return outgoingQueue;
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
My problem is exactly at the second run(9 method where I am getting exception in MessageProcessor class and it loops back to Polling.
How do I implement multithreading here, as when the thread sleeps for 3 seocnds in polling it should simultaneously update the database.
After which, how can the data be fed and updated back into the db.
My program flow - I have three classes:
1.Controller
2.PollerSynchro
3.Msgprocessor
I have database records, which are converted into POJO form and stored in a Collection. With these POJOs my classes try to do multiprocessing and updating in a single stretch.
Controller - has the thread pool, initiates poller class with poll method - done
Poller - should poll for new incoming messages and stores it in incoming queue - done
MsgProcessor - should look for new incoming messages and pass them from outgoing queue to incoming queue - also done
Problem:
Now my problem is
I have to implement this update while the poll thread sleeps for 3 sec,
In my code for the second void run() method in the Poller class, the outgoing queue is not passed and fed to the messageprocessor class for updating. My flow of execution only just loops back to first run method and am getting Class exception.
Please help me to solve these problems.
I can't sugar coat this, your code is a mess. However, as far as why your message processor code is not being executed, you never actually start the thread you created with this code:
MessageProcessor processor = new MessageProcessor() {
// the run method which should fetch the message processor class.
final public void run() {
MessageProcessor(outgoingQueue).generate(outgoingQueue);
}
};
Ignoring the confusingly named method being called, your code should look more like this:
Message processor = new MessageProcessor() {
// the run method which should fetch the message processor class.
final public void run() {
MessageProcessor(outgoingQueue).generate(outgoingQueue);
}
};
new Thread(processor).start();
I'm trying to start a JMXConnectorServer for management and debug purposes. But I don't want this service to prevent application from exiting normally when the last non-daemon thread is terminated.
In other words, I want the following program to terminate immediately:
public class Main {
public static void main(final String[] args) throws IOException {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
JMXServiceURL jmxUrl = new JMXServiceURL("rmi", null, 0);
JMXConnectorServer connectorServer =
JMXConnectorServerFactory.newJMXConnectorServer(jmxUrl, null, mbs);
connectorServer.start();
}
}
I play with similar issue and wrote this class:
public final class HardDaemonizer extends Thread {
private final Runnable target;
private final String newThreadName;
public HardDaemonizer(Runnable target, String name, String newThreadName) {
super(name == null ? "Daemonizer" : name);
setDaemon(true);
this.target = target;
this.newThreadName = newThreadName;
}
#Override
public void run() {
try {
List<Thread> tb = getSubThreads();
target.run();
List<Thread> ta = new java.util.ArrayList<>(getSubThreads());
ta.removeAll(tb);
for (Thread thread : ta) {
thread.setName(newThreadName);
}
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException ex) {
Logger.getLogger(HardDaemonizer.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static Thread daemonize(String daemonizerName, String newThreadName, Runnable target) {
HardDaemonizer daemonizer = new HardDaemonizer(target, daemonizerName, newThreadName);
daemonizer.start();
return daemonizer;
}
private static List<Thread> getSubThreads() {
ThreadGroup group = Thread.currentThread().getThreadGroup().getParent();
Thread[] threads = new Thread[group.activeCount()];
group.enumerate(threads);
return java.util.Arrays.asList(threads);
}
}
You can use it in this way:
HardDaemonizer.daemonize(null, "ConnectorServer", new Runnable(){
#Override
public void run() {
try {
connectorServer.start();
} catch (IOException ex) {
Logger.getLogger(Ralph.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
Be careful - it's tricky!
EDIT
Agh... It's not solution for you. It hard-daemonize connector thread only and this thread will be killed when jvm stops. Additionaly you can customize name of this thread.
Alternatively you can add flag completed and sleep in loop in daemonize method until connector server start up.
SIMPLIFIED
This is simplified daemonizer without tricky thread renaming:
public abstract class Daemonizer<T> extends Thread {
private final T target;
private boolean completed = false;
private Exception cause = null;
public Daemonizer(T target) {
super(Daemonizer.class.getSimpleName());
setDaemon(true);
this.target = target;
}
#Override
public void run() {
try {
act(target);
} catch (Exception ex) {
cause = ex;
}
completed = true;
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException ex) {
java.util.logging.Logger.getLogger(Daemonizer.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
}
public abstract void act(final T target) throws Exception;
public static void daemonize(Daemonizer daemonizer) throws Exception {
daemonizer.start();
while (!daemonizer.completed) {
Thread.sleep(50);
}
if (daemonizer.cause != null) {
throw daemonizer.cause;
}
}
}
Usage:
Daemonizer.daemonize(new Daemonizer<JMXConnectorServer>(server) {
#Override
public void act(JMXConnectorServer server) throws Exception {
server.start();
}
});
Yeah, you will need to so a connectorServer.stop(); at some point.
Edit:
In reading your comments, it sounds like you should do something like:
connectorServer.start();
try {
// create thread-pool
ExecutorService threadPool = Executors...
// submit jobs to the thread-pool
...
threadPool.shutdown();
// wait for the submitted jobs to finish
threadPool.awaitTermination(Long.MAX_LONG, TimeUnit.SECONDS);
} finally {
connectorServer.stop();
}
#Nicholas' idea of the shutdown hook is a good one. Typically, however, I had my main thread wait on some sort of variable that is set from a shutdown() JMX operation. Something like:
public CountDownLatch shutdownLatch = new CountDownLatch(1);
...
// in main
connectorServer.start();
try {
// do the main-thread stuff
shutdownLatch.await();
} finally {
connectorServer.stop();
}
// in some JMX exposed operation
public void shutdown() {
Main.shutdownLatch.countDown();
}
As an aside, you could use my SimpleJMX package to manage your JMX server for you.
JmxServer jmxServer = new JmxServer(8000);
jmxServer.start();
try {
// register our lookupCache object defined below
jmxServer.register(lookupCache);
jmxServer.register(someOtherObject);
} finally {
jmxServer.stop();
}
From my experience, the JMXConnectorServer is only running in a user thread when you create it explicitly.
If you instead configure RMI access for the platform MBean server via system properties, the implicitly created JMX connector server will run as daemon process and not prevent the JMV shutdown. To do this, your code would shrink to the following
public class Main {
public static void main(final String[] args) throws IOException {
MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
}
}
but you'll need to set the following system properties:
-Dcom.sun.management.jmxremote.port=1919
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
You could add a JVM Shutdown Hook to stop the connector server.
===== UPDATE =====
Not sure why your shutdown hook doesn't work. Perhaps you can supply your sample code. Here's an example:
public static void main(String[] args) {
try {
log("Creating Connector Server");
final JMXConnectorServer jcs = JMXConnectorServerFactory.newJMXConnectorServer(new JMXServiceURL("rmi", "localhost", 12387), null, ManagementFactory.getPlatformMBeanServer());
Thread jcsStopper = new Thread("JCS-Stopper") {
public void run() {
if(jcs.isActive()) {
try {
jcs.stop();
log("Connector Server Stopped");
} catch (Exception e) {
log("Failed to stop JCS");
e.printStackTrace();
}
}
}
};
jcsStopper.setDaemon(false);
Runtime.getRuntime().addShutdownHook(jcsStopper);
log("Registered Server Stop Task");
jcs.start();
log("Server Started");
Thread.sleep(3000);
System.exit(0);
} catch (Exception ex) {
ex.printStackTrace(System.err);
}
}
Output is:
[main]:Creating Connector Server
[main]:Registered Server Stop Task
[main]:Server Started
[JCS-Stopper]:Connector Server Stopped
String port = getProperty("com.sun.management.jmxremote.port");
if (port == null) {
port = String.valueOf(getAvailablePort());
System.setProperty("com.sun.management.jmxremote.port", port);
System.setProperty("com.sun.management.jmxremote.ssl", "false");
System.setProperty("com.sun.management.jmxremote.authenticate", "false");
sun.management.Agent.startAgent();
}
log.info(InetAddress.getLocalHost().getCanonicalHostName() + ":" + port);