-
Notifications
You must be signed in to change notification settings - Fork 140
Error handling in QBit
Let's say you have a simple Java class as follows:
@RequestMapping("/my")
public static class MyService {
public boolean methodThatThrowsError() {
puts("Method that throws error");
throw new IllegalStateException("ERROR");
}
public void methodThatThrowsError2(Callback<Boolean> callback) {
puts("Method that throws error");
throw new IllegalStateException("ERROR");
}
public boolean regularMethod() {
puts("Regular method");
return true;
}
}
You want to handle errors from the above. You can. First, in order to handle errors, the service method must have a return or a callback.
The above would equate to this client proxy interface.
public static interface IMyService {
void methodThatThrowsError(Callback<Boolean> callback);
void regularMethod(Callback<Boolean> callback);
void methodThatThrowsError2(Callback<Boolean> callback);
}
The error handling should work the same if you are using a remote websocket proxy, a service bundle or a service as this example shows.
Create a client proxy using a service builder.
final ServiceQueue serviceQueue = ServiceBuilder.serviceBuilder().build(new MyService())
.startServiceQueue().startCallBackHandler();
final IMyService client = serviceQueue
.createProxyWithAutoFlush(IMyService.class, 10, TimeUnit.MILLISECONDS);
Now invoke it.
client.methodThatThrowsError(new Callback<Boolean>() {
@Override
public void accept(Boolean aBoolean) {
//Never called
puts("Called back 1.. callback is here", aBoolean);
}
});
puts("Called back 1");
The accept method is never called because the method throws an exception.
Since the method throws an exception the callback is never called.
If you want to register an exception handler, you could do this.
/* Now to actually handle the error. */
client.methodThatThrowsError(new Callback<Boolean>() {
@Override
public void accept(Boolean aBoolean) {
//Never called
puts("Called back 1.. callback is here", aBoolean);
}
@Override
public void onError(Throwable error) {
//THIS GETS CALLED!
puts("This gets called!", error);
}
});
The moral of the story is that if you want to handle the error, you must register a callback. In order to register a callback, you must have a method that has a callback as the first param or a method
Since this is not very attractive, you could use a reactor like so:
final Reactor reactor = new Reactor(Timer.timer(), 10_000, TimeUnit.MILLISECONDS);
final CallbackBuilder callbackBuilder = CallbackBuilder.callbackBuilder(reactor);
callbackBuilder.setCallback(Boolean.class,
aBoolean ->
{
puts("Return from method", aBoolean);
});
callbackBuilder.setOnError(throwable
-> {
puts("I customized the way I am handling errors");
});
client.regularMethod(callbackBuilder.build());
client.methodThatThrowsError(callbackBuilder.build());
client.methodThatThrowsError2(callbackBuilder.build());
A reactor can handle multiple callbacks and timeouts, and errors. But you have to periodically call reactor process which is no problem if you are running a QBit service. You use a reactor inside of a services' @QueueCallbackHandler handler for limit, empty and idle.
I just added another version of callback builder that works without a reactor so you can do this:
final CallbackBuilder callbackBuilder2 = CallbackBuilder.callbackBuilder();
callbackBuilder2.setCallback(Boolean.class,
aBoolean ->
{
puts("Return from method 2", aBoolean);
});
callbackBuilder2.setOnError(throwable
-> {
puts("I customized the way I am handling errors 2");
});
client.regularMethod(callbackBuilder2.build());
client.methodThatThrowsError(callbackBuilder2.build());
client.methodThatThrowsError2(callbackBuilder2.build());
To register a new callback just call setCallback before build. This will build a new callback with the error handling that you registered with the builder. If you want to customize the error handling just call setOnError before the call.
client.methodThatThrowsError(
CallbackBuilder.callbackBuilder().setCallback(Boolean.class, aBoolean -> puts("Custom callback handler", aBoolean))
.setOnError(throwable -> puts("Custom error handler", throwable)).build()
);
in progress
package io.advantageous.qbit.example.errors;
import io.advantageous.boon.core.Sys;
import io.advantageous.qbit.annotation.RequestMapping;
import io.advantageous.qbit.reactive.Callback;
import io.advantageous.qbit.reactive.CallbackBuilder;
import io.advantageous.qbit.reactive.Reactor;
import io.advantageous.qbit.service.ServiceBuilder;
import io.advantageous.qbit.service.ServiceQueue;
import io.advantageous.qbit.util.Timer;
import java.util.concurrent.TimeUnit;
import static io.advantageous.boon.core.IO.puts;
public class ErrorHandling {
@RequestMapping("/my")
public static class MyService {
public boolean methodThatThrowsError() {
puts("Method that throws error");
throw new IllegalStateException("ERROR");
}
public void methodThatThrowsError2(Callback<Boolean> callback) {
puts("Method that throws error");
throw new IllegalStateException("ERROR");
}
public boolean regularMethod() {
puts("Regular method");
return true;
}
}
public static interface IMyService {
void methodThatThrowsError(Callback<Boolean> callback);
void regularMethod(Callback<Boolean> callback);
void methodThatThrowsError2(Callback<Boolean> callback);
}
public static void main(final String... args) {
final ServiceQueue serviceQueue = ServiceBuilder.serviceBuilder().build(new MyService())
.startServiceQueue().startCallBackHandler();
final IMyService client = serviceQueue
.createProxyWithAutoFlush(IMyService.class, 10, TimeUnit.MILLISECONDS);
client.methodThatThrowsError(new Callback<Boolean>() {
@Override
public void accept(Boolean aBoolean) {
//Never called
puts("Called back 1.. callback is here", aBoolean);
}
});
puts("Called back 1");
/* Since the method throws an exception
the callback is never called.
*/
//No way to catch it. No callback.
client.methodThatThrowsError2(new Callback<Boolean>() {
@Override
public void accept(Boolean aBoolean) {
//Never called
puts("Called back 2 callback 2", aBoolean);
}
});
puts("Called back 2");
/* The default error handling will just output
errors to the log.
For many services where you don't expect exception
or services where there is nothing you can do with exceptions
if they do occur that is exactly what you want.
If all you are going to do is log it, then
we handle that already.
However, sometimes you can recover.
Sometimes, you want to log additional context, so...
*/
/* Now to actually handle the error. */
client.methodThatThrowsError(new Callback<Boolean>() {
@Override
public void accept(Boolean aBoolean) {
//Never called
puts("Called back 1.. callback is here", aBoolean);
}
@Override
public void onError(Throwable error) {
//THIS GETS CALLED!
puts("This gets called!", error);
}
});
/* Now to actually handle the error. */
client.methodThatThrowsError2(new Callback<Boolean>() {
@Override
public void accept(Boolean aBoolean) {
//NEVER CALLED
puts("Called back 1.. callback is here", aBoolean);
}
@Override
public void onError(Throwable error) {
//THIS GETS CALLED!
puts("This gets called!", error);
}
/**
* You can also handle timeouts btw.
*/
@Override
public void onTimeout() {
puts("This does not but can you imagine when it would");
}
});
/* The moral of the story is that if you want to handle
* the error, you must register a callback.
*
* In order to register a callback, you must have a method
* that has a callback as the first param or a method
* that returns a value.
*/
/*
* So far we have been using inner classes.
* Let show an example on how to do the same with lambda.
*/
final Reactor reactor = new Reactor(Timer.timer(), 10_000, TimeUnit.MILLISECONDS);
final CallbackBuilder callbackBuilder = CallbackBuilder.callbackBuilder(reactor);
callbackBuilder.setCallback(Boolean.class,
aBoolean ->
{
puts("Return from method", aBoolean);
});
callbackBuilder.setOnError(throwable
-> {
puts("I customized the way I am handling errors");
});
client.regularMethod(callbackBuilder.build());
client.methodThatThrowsError(callbackBuilder.build());
client.methodThatThrowsError2(callbackBuilder.build());
/** A reactor can handle multiple callbacks and timeouts, and errors. */
for (int index =0; index<10; index++) {
Sys.sleep(10);
reactor.process();
}
/** You use a reactor inside of a services @QueueCallback handler for limit, empty and idle. */
Sys.sleep(1000);
/**
* I just added this.
* You can now use callback builder without reactor.
* Use this when you want to use things outside of a service.
*/
final CallbackBuilder callbackBuilder2 = CallbackBuilder.callbackBuilder();
callbackBuilder2.setCallback(Boolean.class,
aBoolean ->
{
puts("Return from method 2", aBoolean);
});
callbackBuilder2.setOnError(throwable
-> {
puts("I customized the way I am handling errors 2");
});
client.regularMethod(callbackBuilder2.build());
client.methodThatThrowsError(callbackBuilder2.build());
client.methodThatThrowsError2(callbackBuilder2.build());
Sys.sleep(2000);
}
}
QBit Website What is Microservices Architecture?
QBit Java Micorservices lib tutorials
The Java microservice lib. QBit is a reactive programming lib for building microservices - JSON, HTTP, WebSocket, and REST. QBit uses reactive programming to build elastic REST, and WebSockets based cloud friendly, web services. SOA evolved for mobile and cloud. ServiceDiscovery, Health, reactive StatService, events, Java idiomatic reactive programming for Microservices.
Reactive Programming, Java Microservices, Rick Hightower
Java Microservices Architecture
[Microservice Service Discovery with Consul] (http://www.mammatustech.com/Microservice-Service-Discovery-with-Consul)
Microservices Service Discovery Tutorial with Consul
[Reactive Microservices] (http://www.mammatustech.com/reactive-microservices)
[High Speed Microservices] (http://www.mammatustech.com/high-speed-microservices)
Reactive Microservices Tutorial, using the Reactor
QBit is mentioned in the Restlet blog
All code is written using JetBrains Idea - the best IDE ever!
Kafka training, Kafka consulting, Cassandra training, Cassandra consulting, Spark training, Spark consulting
Tutorials
- QBit tutorials
- Microservices Intro
- Microservice KPI Monitoring
- Microservice Batteries Included
- RESTful APIs
- QBit and Reakt Promises
- Resourceful REST
- Microservices Reactor
- Working with JSON maps and lists
__
Docs
Getting Started
- First REST Microservice
- REST Microservice Part 2
- ServiceQueue
- ServiceBundle
- ServiceEndpointServer
- REST with URI Params
- Simple Single Page App
Basics
- What is QBit?
- Detailed Overview of QBit
- High level overview
- Low-level HTTP and WebSocket
- Low level WebSocket
- HttpClient
- HTTP Request filter
- HTTP Proxy
- Queues and flushing
- Local Proxies
- ServiceQueue remote and local
- ManagedServiceBuilder, consul, StatsD, Swagger support
- Working with Service Pools
- Callback Builders
- Error Handling
- Health System
- Stats System
- Reactor callback coordination
- Early Service Examples
Concepts
REST
Callbacks and Reactor
Event Bus
Advanced
Integration
- Using QBit in Vert.x
- Reactor-Integrating with Cassandra
- Using QBit with Spring Boot
- SolrJ and service pools
- Swagger support
- MDC Support
- Reactive Streams
- Mesos, Docker, Heroku
- DNS SRV
QBit case studies
QBit 2 Roadmap
-- Related Projects
- QBit Reactive Microservices
- Reakt Reactive Java
- Reakt Guava Bridge
- QBit Extensions
- Reactive Microservices
Kafka training, Kafka consulting, Cassandra training, Cassandra consulting, Spark training, Spark consulting