netty pipeline order for outbound message - java

I am using Netty v4.1.9final and trying to send a message from client to server. I tried setting up the channel client initializer with the handler as follows on the client side:
final Bootstrap bootstrap = BootstrapGenerator.generate();
bootstrap.handler(new XmlClientInitializer());
XMLClientInitializer
public class XmlClientInitializer extends ChannelInitializer<SocketChannel> {
#Override
public void initChannel(SocketChannel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("fileEncoder", new FileEncoder());
pipeline.addLast("handler", new XmlSenderHandler());
}
}
FileEncoder
public class FileEncoder extends MessageToByteEncoder<String> {
XmlSenderHandler
public class XmlSenderHandler extends ChannelOutboundHandlerAdapter {
private static final Logger log = LogManager.getLogger(XmlSenderHandler.class.getName());
private static ChannelHandlerContext ctx;
#Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
log.info("attempting to write messages to server {}", msg.toString());
ctx.write(msg, promise);
}
#Override
#SuppressWarnings("FutureReturnValueIgnored")
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
super.exceptionCaught(ctx, cause);
}
}
Even though the FileEncoder is defined first in the pipeline, it gets called after the xml handler (which is not what I am seeking). Is this because the FileEncoder is extending MessageToByteEncoder or am I configuring the channel incorrectly?

No, the pipeline is configured correctly. The point is that the inbound event is processed from the first handler to the last, and the outbound event is processed from the last handler to the first one.
ChannelPipeline doc

Related

Can I use multiple JMS listeners in one Component?

I have a component with multiple listeners in one component:
#JmsListener(destination = "${jsa.activemq.topic.stomp}")
public void receiveMessage(final TextMessage msg) throws Exception {
printMessage(msg);
}
#JmsListener(destination = "${jsa.activemq.topic.mqtt}")
public void receiveMessage(final BytesMessage msg) throws Exception {
printMessage(msg);
}
#JmsListener(destination = "${jsa.activemq.topic.mqtt.alert}")
public void receiveAlertMessage(final BytesMessage msg) throws Exception {
printMessage(msg);
}
The first listener (topic.stomp) reads all messages perfectly. Other two don't get messages even if I leave just one.

Netty TCP Socket InputStream

Netty TCP Server is running at port 8000 receiving NMEA format data. It uses Marine API library to convert the gibberish to a meaningful information which needs input stream from the socket.
SentenceReader sentenceReader = new SentenceReader(socket.getInputStream());
sentenceReader.addSentenceListener(new MultiSentenceListener());
sentenceReader.start();
How can i get inputstream for netty server port being used?
SentenceReader does not have any method to accept "streamed in" data, however with subclassing, it can be made to accept the data.
The core of SentenceReader uses a DataReader for its data, normally this datareader is polled from a seperate thread SentenceReader itself, and we can modify this structure to get what we need.
First, we subclass SentenceReader with our own class, give it the proper constructor and methods we want, and remove the effect of the start and stop methods. We provide null as the file for now (and hope future versions provide a method to pass a datareader in directly)
public class NettySentenceReader extends SentenceReader {
public NettySentenceReader () {
super((InputStream)null);
}
#Override
public void start() {
}
#Override
public void stop() {
}
}
We now need to implement all functionality of the internal class DataReader inside our own Netty handler, to replicate the same behaviour
public class SentenceReaderHandler extends
SimpleChannelInboundHandler<String> {
private SentenceFactory factory;
private SentenceReader parent;
public SentenceReaderHandler (SentenceReader parent) {
this.parent = parent;
}
#Override
public void channelRegistered(ChannelHandlerContext ctx) {
if(!ctx.channel().isActive())
return;
//ActivityMonitor monitor = new ActivityMonitor(parent);
this.factory = SentenceFactory.getInstance();
}
#Override
public void channelActive(ChannelHandlerContext ctx) {
//ActivityMonitor monitor = new ActivityMonitor(parent);
this.factory = SentenceFactory.getInstance();
}
#Override
// This method will be renamed to `messageReceived` in Netty 5.0.0
protected void channelRead0(ChannelHandlerContext ctx, String data)
throws Exception {
if (SentenceValidator.isValid(data)) {
monitor.refresh();
Sentence s = factory.createParser(data);
parent.fireSentenceEvent(s);
} else if (!SentenceValidator.isSentence(data)) {
parent.fireDataEvent(data);
}
}
#Override
public void channelInactive(ChannelHandlerContext ctx) {
//monitor.reset();
parent.fireReadingStopped();
}
#Override
public void channelUnregistered(ChannelHandlerContext ctx) {
if(!ctx.channel().isActive())
return;
//monitor.reset();
parent.fireReadingStopped();
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable e) {
parent.handleException("Data read failed", e);
}
}
Finally, we need to integrate this into a Netty pipeline:
SentenceReader reader = new NettySentenceReader();
bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
private static final StringDecoder DECODER = new StringDecoder();
#Override
protected void initChannel(SocketChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
pipeline.addLast(DECODER);
pipeline.addLast(new SentenceReaderHandler(reader));
}
});
You can't easily as InputStream is blocking and netty is an async - non blocking API.

Efficient channelRead for Java Netty Server

I'm using netty to develop a proxy server and my proxy ProxyBackendHandler class is as follows. There on channelRead method I need to get the msg data and write to client as TextWebSocketFrame. To do that I have used a StringBuilder and a while loop to iterate the ByteBuf. Can anyone suggest me a better way to do this as it seems that above code has high perfomance overhead when the high data loads.
public class ProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
StringBuilder sReplyBuffer;
public ProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
sReplyBuffer = new StringBuilder(4000);
}
#Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
// Please suggest a efficient implementation for read msg and pass it to writeAndFlush.
ByteBuf in = (ByteBuf) msg;
sReplyBuffer.setLength(0);
try {
while (in.isReadable()) {
sReplyBuffer.append((char) in.readByte());
}
} finally {
((ByteBuf) msg).release();
}
inboundChannel.writeAndFlush(new TextWebSocketFrame (sReplyBuffer.toString())).addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
ctx.channel().read();
System.out.println("Sent To Client");
} else {
future.channel().close();
}
}
});
}
}
Maybe something like this:
public class ProxyBackendHandler extends ChannelInboundHandlerAdapter {
private final Channel inboundChannel;
public ProxyBackendHandler(Channel inboundChannel) {
this.inboundChannel = inboundChannel;
}
#Override
public void channelRead(final ChannelHandlerContext ctx, Object msg) {
inboundChannel.writeAndFlush(new TextWebSocketFrame((ByteBuf) msg)).addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) {
if (future.isSuccess()) {
ctx.channel().read();
System.out.println("Sent To Client");
} else {
future.channel().close();
}
}
});
}
}
I suggest not using a StringBuilder at all. Just use the buffer you already have. You don't state what a TextWebSocketFrame might be, or why you think you need it, but ultimately a proxy server only has to copy bytes. You don't need StringBuilders or extra classes for that. Or Netty, frankly.

MessageEvent cannot be resolved

I've used the all-in-one jar from Netty, but MessageEvent cannot be resolved:
import io.netty.channel.MessageEvent;
GameServer.java
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(
new LoggingHandler(LogLevel.INFO),
new GameServerHandler());
ch.pipeline().addLast("protobufHandler", new MessagesHandler());
}
Also, if I remove the MessageEvent to make it work, I can't override it.
public class MessageHandler extends SimpleChannelInboundHandler<Object> {
#Override
public void messageReceived(ChannelHandlerContext arg0, MessageEvent msg) {
// Print msg
}
}
I'm using the latest release, what I'm doing wrong?
There is no class called MessageEvent in Netty 4. I think you want to replace MessageEvent with Object in your MessageHandler.

Looking for substitutes for some functionality while moving from Netty 3.x to 4.21 final

In Netty 3.5 we use SimpleChannelHandler, which provides method for both event types. How do I use the same approach in Netty 4.0.0?
To be more specific i m looking for a substitute of the method below
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
}
I am trying to send message to all clients connected to the server.
Here is the example for netty 3.x
ChannelGroup allConnected = new DefaultChannelGroup("all-connected");
#Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
super.channelConnected(ctx, e);
allConnected.add(e.getChannel());
}
and then to send messages to all channels connected
ChannelBuffer cb = ChannelBuffers.wrappedBuffer("hello".getBytes(Charset.forName("UTF-8")));
allConnected.write(cb);
This is what i need to do in Netty 4.21 final but i couldn't find a similar method which provided me the needed functionality.
I'm not really sure what you mean by both event types. I guess you mean client and server? I use ChannelInboundHandlerAdapter for that:
public class ServerCommunicationHandler extends ChannelInboundHandlerAdapter {
private final ChannelGroup channels = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
channels.add(ctx.channel());
ctx.fireChannelActive();
}
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
broadcastMessage(msg);
ctx.fireChannelRead(msg);
}
#Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
channels.remove(ctx.channel());
ctx.fireChannelInactive();
}
public void broadcastMessage(Object object) {
channels.writeAndFlush(object);
}
}

Categories