Reputation: 377
I am using Guava EventBus in sync. How can I rollback the complete transaction if any of the subscribers throw an Exception? How can I throw an Exception which will not be caught by the EventBus Subscriber?
Upvotes: 1
Views: 1965
Reputation: 377
I solved it using java.lang.ThreadLocal
variable across the publisher and the subscriber.
Publisher needs to be wrapped in a class which reads thread local exception and throws it
public void publish(Event event) {
eventBus.post(event);
if(threadLocalException != null) {
Store threadLocalException in some variable say e
Clear threadLocalException variable
throw e;
}
}
Subscriber needs to be wrapped in a class to set the exception in thread local variable
public abstract class EventSubscriber<T extends Event> {
@Subscribe
public void invoke(T event) {
try {
handle(event);
} catch (Exception e) {
Set thread local variable to e
}
}
protected abstract void handle(T event);
}
Upvotes: 0
Reputation: 461
Inherit EventBus and make your own eventBus which will throw exception. The package must be com.google.common.eventbus for handleSubscriberException is an internal method.
package com.google.common.eventbus;
import com.google.common.util.concurrent.MoreExecutors;
/**
* A eventbus wihch will throw exceptions during event handle process.
* @author ytm
*
*/
public class ErrorThrowEventBus extends EventBus {
/**
* Creates a new EventBus with the given {@code identifier}.
*
* @param identifier a brief name for this bus, for logging purposes. Should be a valid Java
* identifier.
*/
public ErrorThrowEventBus(String identifier) {
super(
identifier,
MoreExecutors.directExecutor(),
Dispatcher.perThreadDispatchQueue(),
LoggingHandler.INSTANCE);
}
/**
* Creates a new EventBus with the given {@link SubscriberExceptionHandler}.
*
* @param exceptionHandler Handler for subscriber exceptions.
* @since 16.0
*/
public ErrorThrowEventBus(SubscriberExceptionHandler exceptionHandler) {
super(
"default",
MoreExecutors.directExecutor(),
Dispatcher.perThreadDispatchQueue(),
exceptionHandler);
}
/**
* Just throw a EventHandleException if there's any exception.
* @param e
* @param context
* @throws EventHandleException
*/
@Override
void handleSubscriberException(Throwable e, SubscriberExceptionContext context) throws EventHandleException {
throw new EventHandleException(e);
}
}
Upvotes: 0
Reputation: 12140
All you have to do is to look at the source code of Guava's EventBus
class.
Let's start from the end:
How can I throw an Exception which will not be caught by the EventBus Subscriber?
Subscribers' methods are called in sequence, one after another, by com.google.common.eventbus.Dispatcher#dispatch
method. To call methods of your Subscribers, EventBus use reflection's method Method#invoke
which, in turn, throws InvocationTargetException
if called method throws an exception.
As you can also see, InvocationTargetException
(which will be wrapped around your Exception
) is handled as follows:
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
}
throw e;
}
at the upper level, exception is handled like that:
try {
invokeSubscriberMethod(event);
} catch (InvocationTargetException e) {
bus.handleSubscriberException(e.getCause(), context(event));
}
TL;DR
So, the only way to omit EventBus
exception handler is to throw not Exception
, but Error
in your subscribing method - what is certainly a bad practise.
How can I rollback the complete transaction if any of the subscribers throw an Exception?
EventBus
exception handler handles exceptions by calling com.google.common.eventbus.EventBus#handleSubscriberException
method. It looks like this:
try {
exceptionHandler.handleException(e, context);
} catch (Throwable e2) {
// logging
}
So, any exceptions thrown from exception handler will not help. You have two choices:
EventBus
exception handler.Upvotes: 1