Netty.io messageReceived override without instanceof - java

In netty, MessageEvent (wrapper for messages) has a method Object getMessage() to get the real carried message from the network. Reading the source I noticed they heavily use the instanceof operator to switch among methods.
However, having a wide variety of message types I would like to avoid a method like this:
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
if (e.getMessage() instanceof MessageType1) {
...
} else if (e.getMessage() instanceof MessageType2) {
...
} ... {
...
} else if (e.getMessage() instanceof MessageTypeN) {
...
} else {
ctx.sendUpstream(e);
}
}
Writing different methods taking advantage of polymorphism would be much better, like:
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
// MessageType implements Message
if (e.getMessage() instanceof Message) {
handleMessage(ctx, (Message) e.getMessage());
} else {
ctx.sendUpstream(e);
}
}
void handleMessage(ChannelHandlerContext ctx, MessageType1 m) {
...
}
...
void handleMessage(ChannelHandlerContext ctx, MessageTypeN m) {
...
}
But i cannot due to downcasting limitations. Is there a clean way to do this or am I tied to instanceof cascade?
I could bring the logic out the Handler using .doSomething() methods inside Message sub-types but I'd like to keep the business logic inside the netty pipeline.

Solved applying Visitor Pattern:
public interface Handler {
void handleMessage(ChannelHandlerContext ctx, MessageType1 m);
void handleMessage(ChannelHandlerContext ctx, MessageType2 m);
...
void handleMessage(ChannelHandlerContext ctx, MessageTypeN m);
}
then:
#Sharable
public class MessageHandler extends SimpleChannelHandler implements Handler {
...
#Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
if (e.getMessage() instanceof Message) {
Message m = (Message) e.getMessage();
m.handleTo(ctx, this);
} else {
ctx.sendUpstream(e);
}
}
#Override
public void handleMessage(ChannelHandlerContext ctx, MessageType1 m) {
...
}
and
public interface Message {
/*
* Will be {
* handler.handleMessage(ctx, this);
* }
* everywhere.
*/
void handleTo(ChannelHandlerContext ctx, Handler handler);
}

Related

The exception was not handled due to missing onError handler in the subscribe() method call

I am calling the request it self in a base class
#Override
public Single<BaseResponse<D>> call() {
Single<BaseResponse<D>> singleResponse = Single.create(emitter -> request().getAsOkHttpResponseAndParsed(typeToken(), new OkHttpResponseAndParsedRequestListener<BaseData<D>>() {
#Override
public void onResponse(Response okHttpResponse, BaseData<D> response) {
try {
BaseResponse<D> r = new BaseResponse<D>(okHttpResponse) {
};
r.setData(response.getData());
r.setStatus(response.getStatus());
emitter.onSuccess(r);
} catch (Throwable throwable) {
Log.e("error", "throwable");
emitter.onError(throwable);
}
}
#Override
public void onError(ANError anError) {
Log.e("error", "error");
emitter.onError(anError);
}
}));
singleResponse.doOnSubscribe(__ ->
EspressoIdlingResource.countingIdlingResource.increment()).
doFinally(EspressoIdlingResource.countingIdlingResource::decrement).subscribe();
return singleResponse;
}
then I have my single observer builder class
public class SingleObserverBuilder<T> extends BaseObserverBuilder<T, SingleObserver<T>> {
private final Consumer<Disposable> disposableConsumer;
public SingleObserverBuilder(#NonNull Consumer<Disposable> disposableConsumer) {
this.disposableConsumer = disposableConsumer;
}
#Override
public SingleObserver<T> build() {
return new SingleObserver<T>() {
#Override
public void onSubscribe(Disposable d) {
disposableConsumer.accept(d);
}
#Override
public void onSuccess(T t) {
if (onSuccessListeners() != null) {
for (Consumer<T> onSuccess : onSuccessListeners()) {
onSuccess.accept(t);
}
}
}
#Override
public void onError(Throwable e) {
Log.e("error", "base error");
if (onFailureListeners() != null)
for (Consumer<Throwable> onFailure : onFailureListeners()) {
onFailure.accept(e);
}
}
};
}
}
and in my base observer fragment I access this single observer
public abstract class BaseObserverFragment extends Fragment {
protected CompositeDisposable disposable = new CompositeDisposable();
protected <T> SingleObserverBuilder<T> getDefaultSingleObserver(Object tag) {
SingleObserverBuilder<T> builder = new SingleObserverBuilder<>(disposable -> this.disposable.add(disposable));
builder.tag(tag).onSuccess(object -> {
Logger.e(builder.tag().toString(), "onSuccess");
}).onFailure(throwable -> {
Log.e("error", "base fragment");
Logger.e(builder.tag().toString(), "onFailure");
ErrorLogger.log(getContext(), builder.tag().toString(), throwable);
});
return builder;
}
#Override
public void onDetach() {
super.onDetach();
if(!disposable.isDisposed()) {
Log.e("error", "detach");
disposable.clear();
disposable.dispose();
}
}
}
then in the fragment itself that's how I call api request
viewModel.setAvailabilityStatus(isAvailable)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this.<BaseResponse<ChangeSpAvailabilityResponse>>getDefaultSingleObserver(OperationTag.CHANGE_AVAILABILITY)
.onSuccess(response -> {
})
.onFailure( throwable -> {
Log.e("error", "last");
if (throwable instanceof ANError) {
Log.e("error", "throwable");
if (((ANError) throwable).getErrorCode() == 400) {
((ANError) throwable).getErrorBody();
handleChangeAvailabilityResponse(((ANError) throwable).getErrorAsObject(ChangeSpAvailabilityResponse.class));
}
}
}).build());
}
in that api request I wanna handle failure request body and it all work fine, but sometimes I get this error :
The exception was not handled due to missing onError handler in the
subscribe() method call. Further reading:
https://github.com/ReactiveX/RxJava/wiki/Error-Handling |
com.androidnetworking.error.ANError
and I don't know why or what should I do to handle error
also I have this line
RxJavaPlugins.setErrorHandler(throwable -> {
Log.e("error", throwable.getMessage());
});
in my application class

Server does not realize the message sent by client in Netty 4.x

I try to learn Netty 4.x by simply implementing the following example. I have a server and a client. At a point of time, the client wants to know the current date-time and he will ask the server "What time is it?". When the server realizes the question, he will reply with the current date-time.
My implementation is as following
TimeClientInboundHandler.java
public class TimeClientInboundHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
try {
long currentTimeMillis = (byteBuf.readUnsignedInt() - 2208988800L) * 1000L;
System.out.println(new Date(currentTimeMillis));
ctx.close();
} finally {
byteBuf.release();
}
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.write("What time is it?");
ctx.flush();
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
TimeServerInboundHandler.java
public class TimeServerInboundHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = (ByteBuf) msg;
try {
byteBuf.readCharSequence(1024, StandardCharsets.UTF_8);
System.out.println(byteBuf.toString());
} finally {
((ByteBuf) msg).release();
}
}
#Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
final ByteBuf byteBuf = ctx.alloc().buffer(4);
byteBuf.writeInt((int) (System.currentTimeMillis() / 1000L + 2208988800L));
final ChannelFuture f = ctx.writeAndFlush(byteBuf);
f.addListener(new ChannelFutureListener() {
#Override
public void operationComplete(ChannelFuture future) throws Exception {
assert f == future;
ctx.close();
}
});
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
}
However, I did not get the expecting result. Specifically, on the server-side, the question "What time is it?" has not been printed out on console.
What wrong did I implement?
Reason
There are two reasons for this problem.
Server-side cannot realize the message sent from the client-side because the message is written (from the client-side) in the wrong way. Instead of ctx.write("What time is it?");, we should implement as following ctx.write(Unpooled.coppiedBuffer("What time is it?", StandardCharsets.UTF_8);
Furthermore, in TimeServerInboundHandler.java, we should remove the method channelActive(). This is because channelRead() will be never triggered, but of course channelActive() when the channel is active.
Solution
TimeClientInboundHandler.java
public class TimeClientInboundHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf byteBuf = (ByteBuf) msg;
try {
System.out.println("Client received: " + byteBuf.toString(StandardCharsets.UTF_8));
ctx.close();
} finally {
byteBuf.release();
}
}
#Override
public void channelActive(ChannelHandlerContext ctx) {
ctx.writeAndFlush(Unpooled.copiedBuffer("What time is it?", StandardCharsets.UTF_8));
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
TimeServerInboundHandler.java
public class TimeServerInboundHandler extends ChannelInboundHandlerAdapter {
#Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf byteBuf = (ByteBuf) msg;
try {
System.out.println("Server received: " + byteBuf.toString(StandardCharsets.UTF_8));
String answer = Utils.getCurrentTime();
ctx.writeAndFlush(Unpooled.copiedBuffer(answer, StandardCharsets.UTF_8));
} finally {
((ByteBuf) msg).release();
}
}
#Override
public void channelReadComplete(ChannelHandlerContext ctx) {
ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)
.addListener(ChannelFutureListener.CLOSE);
}
#Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
cause.printStackTrace();
ctx.close();
}
}
Utils.java
public class Utils {
public static String getCurrentTime() {
long currentTimeMillis = ((System.currentTimeMillis() / 1000L + 2208988800L) - 2208988800L) * 1000L;
return new Date(currentTimeMillis).toString();
}
}

jmDNS ServiceListener triggers after being removed

I used jmDNS to find some services on wifi network.
The problem is that sometimes after service is resolved, and I remove this listener, it triggers again with serviceResolved() method. jmDNS 3.4.1
private static JmDNS jmDNS;
private static ServiceType currentType;
private static ServiceListener currentListener;
public static Observable<String> getXXXIp(final ServiceType type,
final WifiManager wifiManager) {
return Observable.create(new Observable.OnSubscribe<String>() {
#Override public void call(final Subscriber<? super String> subscriber) {
InetAddress inetAddress = WifiUtils.getDeviceIpAddress(wifiManager.getConnectionInfo());
try {
jmDNS = JmDNS.create(inetAddress);
currentType = type;
currentListener = new ServiceListener() {
#Override public void serviceAdded(ServiceEvent event) { ... }
#Override public void serviceRemoved(ServiceEvent event) { }
#Override public void serviceResolved(ServiceEvent event) { ... }
};
jmDNS.addServiceListener(type.getCode(), currentListener);
} catch (IOException e) {
LOGGER.error("Exception occurred while searching " + type.getCode(), e);
subscriber.onError(e);
}
}
}).doOnNext(new Action1<String>() {
#Override public void call(String s) {
finish();
}
}).doOnError(new Action1<Throwable>() {
#Override public void call(Throwable throwable) {
finish();
}
});
}
private static void finish() {
if (jmDNS != null) {
LOGGER.info("jmDNS -> close");
try {
jmDNS.removeServiceListener(currentType.getCode(), currentListener);
jmDNS.close();
} catch (IOException e) {
LOGGER.error("Cannot close jmDNS ", e);
}
jmDNS = null;
currentListener = null;
}
}
It is a little complicated logic of service check, so I cut it off, but the main thing that's matter is in one moment onNext() is called once so method finish() is called. And I checked hashcode for listener which I've nullified in finish() and when after this listener onServiceResolved() is called I check it hashcode and it is the same. What is going on? Any ideas?

Akka java never close over a ActorRef

I dont understand this statement about closing over the actor ref in the callback.
Currently I am using
public void onReceive(Object message) throws Exception {
ActorRef senderActorRef = getSender(); //never close over a future
if (message instanceof String) {
Future<String> f =akka.dispatch.Futures.future(new Callable<String>() {
public String call() {
String value= jedisWrapper.getString("name");
senderActorRef.tell((String) message,ActorRef.noSender());
return "what";
}
}, ex);
f.onSuccess(new OnSuccessExtension(), ex);
}
}
private final class OnSuccessExtension extends OnSuccess {
#Override
public void onSuccess(Object arg0) throws Throwable {
log.info("what");
}
}
Is this the right way to use it?
How can I pass the Sender Actor ref in the OnSuccess method?
Also whats the difference between onSuccess and OnComplete ?
If I want to use onComplete how would I use it?
Answer: Pass the Sender Actor Ref in the constructor. The answer given by another user.
OnSuccess is a specialized form of OnComplete.
OnComplete useage from Akka docs
final ExecutionContext ec = system.dispatcher();
future.onComplete(new OnComplete<String>() {
public void onComplete(Throwable failure, String result) {
if (failure != null) {
//We got a failure, handle it here
} else {
// We got a result, do something with it
}
}
}, ec);
Pass it in the constructor:
public void onReceive(Object message) throws Exception {
final ActorRef senderActorRef = getSender(); //never close over a future
if (message instanceof String) {
Future<String> f = // ...
f.onSuccess(new OnSuccessExtension(senderActorRef), ex);
}
}
private final class OnSuccessExtension extends OnSuccess {
private final ActorRef senderActorRef;
public OnSuccessExtension(ActorRef senderActorRef) {
this.senderActorRef = senderActorRef;
}
#Override
public void onSuccess(Object arg0) throws Throwable {
log.info("what");
// use senderActorRef
}
}

Java casting (dynamic)

I'm sure this is a very stupid question, but still I would like to know, is it possible to cast the global variable cause dynamically, in other words without using the instanceof operator ?
The reason for this question is, I feel the instanceof operator is not doing anything great here, it's just casting the cause statically, but in either case it's creating a new IOException(cause)
Because the cause is of type Object, I had to type cast it to either String or Throwable.
private Object cause; // global variable
//...
if (failed)
throw cause instanceof String ? new IOException((String) cause) : new IOException((Throwable) cause);
Below is the actual code snippet where the two overridden methods will be called asynchronously.
public class Command implements ResponseListener {
private Object cause;
// ...
#Override
public void messageReceived(String message, String status) {
// ...
if (!status.equals(MyConstants.COMPLD_MSG)) {
this.cause = status + " received for " + command.split(":")[0] + message;
this.failed = true;
}
doNotify();
}
#Override
public void exceptionCaught(Throwable cause) {
this.cause = cause;
this.failed = true;
doNotify();
}
public void waitForResponse(int cmdTimeout) throws IOException, InterruptedException {
// ...
if (failed)
throw cause instanceof String ? new IOException((String) cause) : new IOException((Throwable) cause);
}
}
Why not having always a Throwable for your cause variable ? Throwable seems more adapted to express a failure than a String. Plus it avoids you to use the "ugly" operator instanceof.
public class Command implements ResponseListener {
private Throwable cause;
// ...
#Override
public void messageReceived(String message, String status) {
// ...
if (!status.equals(MyConstants.COMPLD_MSG)) {
this.cause = new Throwable(status + " received for " + command.split(":")[0] + message);
this.failed = true;
}
doNotify();
}
#Override
public void exceptionCaught(Throwable cause) {
this.cause = cause;
this.failed = true;
doNotify();
}
public void waitForResponse(int cmdTimeout) throws IOException, InterruptedException {
// ...
if (failed)
throw new IOException(cause);
}
}
Update after discussion below:
public class Command implements ResponseListener {
private String cause;
// ...
#Override
public void messageReceived(String message, String status) {
// ...
if (!status.equals(MyConstants.COMPLD_MSG)) {
this.cause = status + " received for " + command.split(":")[0] + message;
this.failed = true;
}
doNotify();
}
#Override
public void exceptionCaught(Throwable cause) {
if(cause.getMessage().isEmpty()) {
this.cause = cause.toString();
}
else {
this.cause = cause.getMessage();
}
this.failed = true;
doNotify();
}
public void waitForResponse(int cmdTimeout) throws IOException, InterruptedException {
// ...
if (failed)
throw new IOException(cause);
}
}
As there is no common superclass of String and Throwable that would be acceptable as parameter to IOException, you have to cast it to one or the other, and in order to determine what to cast it to, you have to use instanceof.
Class.cast and Class.isAssignableFrom methods may be used as dynamic counterparts of cast and instanceof operators respectively.

Categories

Resources