im getting confused that why eventbus need threadlocal here?
/** queues of events for the current thread to dispatch */
private final ThreadLocal<Queue<EventWithHandler>> eventsToDispatch =
new ThreadLocal<Queue<EventWithHandler>>() {
#Override protected Queue<EventWithHandler> initialValue() {
return new LinkedList<EventWithHandler>();
}
};
JUST FOR AsycEventBus to override the method enqueueEvent and dispatchQueuedEvents ??
It's used to handle reentrant posting of events--for example, if one event subscriber posts one or more other events, they'll go in to the queue. Then those events can be posted to their subscribers after the event bus finishes handling the current event.
Related
I was reading about Spring Integration's FluxMessageChannel here and here, but I still don't understand exactly what are the differences between using a DirectChannel and FluxMessageChannel when using Project Reactor. Since the DirectChannel is stateless and controlled by its pollers, I'd expect the FluxMessageChannel to not be needed. I'm trying to understand when exactly should I use each and why, when speaking on Reactive Streams applications that are implemented with Spring Integration.
I currently have a reactive project that uses DirectChannel, and it seems to work fine, even the documentation says:
the flow behavior is changed from an imperative push model to a reactive pull model
I'd like to understand when to use each of the channels and what is the exact difference when working with Reactive Streams.
The DirectChannel does not have any poller and its implementation is very simple: as long as a message is sent to it, the handler is called. In the same caller's thread:
public class DirectChannel extends AbstractSubscribableChannel {
private final UnicastingDispatcher dispatcher = new UnicastingDispatcher();
private volatile Integer maxSubscribers;
/**
* Create a channel with default {#link RoundRobinLoadBalancingStrategy}.
*/
public DirectChannel() {
this(new RoundRobinLoadBalancingStrategy());
}
Where that UnicastingDispatcher is:
public final boolean dispatch(final Message<?> message) {
if (this.executor != null) {
Runnable task = createMessageHandlingTask(message);
this.executor.execute(task);
return true;
}
return this.doDispatch(message);
}
(There is no executor option for the DirectChannel)
private boolean doDispatch(Message<?> message) {
if (tryOptimizedDispatch(message)) {
return true;
}
...
protected boolean tryOptimizedDispatch(Message<?> message) {
MessageHandler handler = this.theOneHandler;
if (handler != null) {
try {
handler.handleMessage(message);
return true;
}
catch (Exception e) {
throw IntegrationUtils.wrapInDeliveryExceptionIfNecessary(message,
() -> "Dispatcher failed to deliver Message", e);
}
}
return false;
}
That's why I call it " imperative push model". The caller is this case is going to wait until the handler finishes its job. And if you have a big flow, everything is going to be stopped in the sender thread until a sent message has reached the end of the flow of direct channels. In two simple words: the publisher is in charge for the whole execution and it is blocked in this case. You haven't faced any problems with your solution based on the DirectChannel just because you didn't use reactive non-blocking threads yet like Netty in WebFlux or MongoDB reactive driver.
The FluxMessageChannel was really designed for Reactive Streams purposes where the subscriber is in charge for handling a message which it pulls from the Flux on demand. This way just after sending the publisher is free to do anything else. Just because it is already a subscriber responsibility to handle the message.
I would say it is definitely OK to use DirectChannel as long as your handlers are not blocking. As long as they are blocking you should go with FluxMessageChannel. Although don't forget that there are other channel types for different tasks: https://docs.spring.io/spring-integration/docs/current/reference/html/core.html#channel-implementations
I'am new to Vaadin and java web applications in general.
I tried to use event bus from google, this is my setup
Event bus singleton class:
public class Bus {
protected static final EventBus eventBus = new EventBus(); //single instance
public static EventBus getBus() {
return eventBus;
}
}
Place where i post my event:
public class MainMenuElementMain extends HorizontalLayout {
Bus eventbus = new Bus();
public MainMenuElementMain(String sectionTitle) {
ClickedMainMenuElement event = new ClickedMainMenuElement(sectionTitle);
eventbus.getBus().post(event);
}
}
Place where i handle the event:
#Subscribe
public void menuElementClicked (ClickedMainMenuElement event) {
add.(someComponent);
}
Everything is O.K. up to the time when i post the event from other web browser, than i got one of those two errors:
IllegalStateException: Can't move a node from one state tree to another
or
IllegalStateException: Cannot access state in VaadinSession or UI without locking the session.
I did some debugging and it seems that each time I refresh the browser or open new tab, the new instance of #Subscribe is register into the Event buss. In conclusion when i post the event all of the #Subscribe methods, from different sessions are called, which cause the error that i mentioned above.
It seems that my eventBus is a singleton sheared through all the sessions, What would be the best approach to make one instance of Event Buss for one session ?
I am trying to persist state machine when event is sent. To do so, I am using
the following code.
engine.getStateMachineAccessor()
.doWithAllRegions(item -> {
item.addStateMachineInterceptor(new StateMachineInterceptorAdapter<String, String>() {
#Override
public void preStateChange(State state,
Message message,
Transition transition,
StateMachine stateMachine)
{
_Logger.info("In the pre-state change stage");
}
From the documentation, StateContext is only available with preTransition and postTransition events, is there any way to obtain it with preStateChange event?
I need to create a queue where when an item is added to the queue a subscriber should be notified. Only solution comes to my brain is to use a while loop to poll the queue or use peek the queue compare whether a new object is added to the queue, if new item found then use onNext() to forward the item to subscriber. It would be an endless loop and that is what I need to observe the queue.
You could sub-class one of the queues and have the add/offer/etc... methods call your Observable.
public class SubscriberQueue<E> extends LinkedList<E> {
private Observable<E> mObservable = PublishSubscriber.create();
#Override
public boolean add(E e) {
if (super.add()) {
mObservable.onNext(e);
}
}
public Observable<E> getObservable() {return mObservable;}
}
// Test code
SubscriberQueue<Integer> myQueue = new SubscriberQueue<>();
myQueue.getObservable().subscribe(i -> System.out.println("Added: " + i));
myQueue.add(1);
On the client side javascript I have
stomp.subscribe("/topic/path", function (message) {
console.info("message received");
});
And on the server side
public class Controller {
private final MessageSendingOperations<String> messagingTemplate;
ï¼ Autowired
public Controller(MessageSendingOperations<String> messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
#SubscribeMapping("/topic/path")
public void subscribe() {
LOGGER.info("before send");
messagingTemplate.convertAndSend(/topic/path, "msg");
}
}
From this setup, I am occasionally (around once in 30 page refreshes) experiencing message dropping, which means I can see neither "message received" msg on the client side nor the websocket traffic from Chrome debugging tool.
"before send" is always logged on the server side.
This looks like that the MessageSendingOperations is not ready when I call it in the subscribe() method. (if I put Thread.sleep(50); before calling messagingTemplate.convertAndSend the problem would disappear (or much less likely to be reproduced))
I wonder if anyone experienced the same before and if there is an event that can tell me MessageSendingOperations is ready or not.
The issue you are facing is laying in the nature of clientInboundChannel which is ExecutorSubscribableChannel by default.
It has 3 subscribers:
0 = {SimpleBrokerMessageHandler#5276} "SimpleBroker[DefaultSubscriptionRegistry[cache[0 destination(s)], registry[0 sessions]]]"
1 = {UserDestinationMessageHandler#5277} "UserDestinationMessageHandler[DefaultUserDestinationResolver[prefix=/user/]]"
2 = {SimpAnnotationMethodMessageHandler#5278} "SimpAnnotationMethodMessageHandler[prefixes=[/app/]]"
which are invoked within taskExecutor, hence asynchronously.
The first one here (SimpleBrokerMessageHandler (or StompBrokerRelayMessageHandler) if you use broker-relay) is responsible to register subscription for the topic.
Your messagingTemplate.convertAndSend(/topic/path, "msg") operation may be performed before the subscription registration for that WebSocket session, because they are performed in the separate threads. Hence the Broker handler doesn't know you to send the message to the session.
The #SubscribeMapping can be configured on method with return, where the result of this method will be sent as a reply to that subscription function on the client.
HTH
Here is my solution. It is along the same lines. Added a ExecutorChannelInterceptor and published a custom SubscriptionSubscribedEvent. The key is to publish the event after the message has been handled by AbstractBrokerMessageHandler which means the subscription has been registered with the broker.
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ExecutorChannelInterceptorAdapter() {
#Override
public void afterMessageHandled(Message<?> message, MessageChannel channel, MessageHandler handler, Exception ex) {
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(message);
if (accessor.getMessageType() == SimpMessageType.SUBSCRIBE && handler instanceof AbstractBrokerMessageHandler) {
/*
* Publish a new session subscribed event AFTER the client
* has been subscribed to the broker. Before spring was
* publishing the event after receiving the message but not
* necessarily after the subscription occurred. There was a
* race condition because the subscription was being done on
* a separate thread.
*/
applicationEventPublisher.publishEvent(new SessionSubscribedEvent(this, message));
}
}
});
}
A little late but I thought I'd add my solution. I was having the same problem with the subscription not being registered before I was sending data through the messaging template. This issue happened rarely and unpredictable because of the race with the DefaultSubscriptionRegistry.
Unfortunately, I could not just use the return method of the #SubscriptionMapping because we were using a custom object mapper that changed dynamically based on the type of user (attribute filtering essentially).
I searched through the Spring code and found SubscriptionMethodReturnValueHandler was responsible for sending the return value of subscription mappings and had a different messagingTemplate than the autowired SimpMessagingTemplate of my async controller!!
So the solution was autowiring MessageChannel clientOutboundChannel into my async controller and using that to create a SimpMessagingTemplate. (You can't directly wire it in because you'll just get the template going to the broker).
In subscription methods, I then used the direct template while in other methods I used the template that went to the broker.