com.microsoft.azure.servicebus.amqp.ReactorDispatcher Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of azure-servicebus Show documentation
Show all versions of azure-servicebus Show documentation
Java library for Azure Service Bus
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.microsoft.azure.servicebus.amqp;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.Pipe;
import java.util.concurrent.ConcurrentLinkedQueue;
import org.apache.qpid.proton.engine.BaseHandler;
import org.apache.qpid.proton.engine.Event;
import org.apache.qpid.proton.Proton;
import org.apache.qpid.proton.reactor.Reactor;
import org.apache.qpid.proton.reactor.Selectable;
import org.apache.qpid.proton.reactor.Selectable.Callback;
/**
* {@link Reactor} is not thread-safe - all calls to {@link Proton} API's should be - on the Reactor Thread.
* {@link Reactor} works out-of-box for all event driven API - ex: onReceive - which could raise upon onSocketRead.
* {@link Reactor} didn't support API's like Send() out-of-box - which could potentially run on different thread to that of Reactor.
* So, the following utility class is used to generate an Event to hook into {@link Reactor}'s event delegation pattern.
* It uses a {@link Pipe} as the IO on which Reactor Listens to.
* Cardinality: multiple {@link ReactorDispatcher}'s could be attached to 1 {@link Reactor}.
* Each {@link ReactorDispatcher} should be initialized Synchronously - as it calls API in {@link Reactor} which is not thread-safe.
*/
public final class ReactorDispatcher {
private final Reactor reactor;
private final Pipe ioSignal;
private final ConcurrentLinkedQueue workQueue;
private final ScheduleHandler workScheduler;
public ReactorDispatcher(final Reactor reactor) throws IOException {
this.reactor = reactor;
this.ioSignal = Pipe.open();
this.workQueue = new ConcurrentLinkedQueue<>();
this.workScheduler = new ScheduleHandler();
initializeSelectable();
}
private void initializeSelectable() {
Selectable schedulerSelectable = this.reactor.selectable();
schedulerSelectable.setChannel(this.ioSignal.source());
schedulerSelectable.onReadable(this.workScheduler);
schedulerSelectable.onFree(new CloseHandler());
schedulerSelectable.setReading(true);
this.reactor.update(schedulerSelectable);
}
public void invoke(final DispatchHandler timerCallback) throws IOException {
this.workQueue.offer(timerCallback);
this.signalWorkQueue();
}
public void invoke(final int delay, final DispatchHandler timerCallback) throws IOException {
this.workQueue.offer(new DelayHandler(this.reactor, delay, timerCallback));
this.signalWorkQueue();
}
private void signalWorkQueue() throws IOException {
try {
this.ioSignal.sink().write(ByteBuffer.allocate(1));
} catch (ClosedChannelException ignorePipeClosedDuringReactorShutdown) {
}
}
private static final class DelayHandler extends BaseHandler {
final int delay;
final BaseHandler timerCallback;
final Reactor reactor;
DelayHandler(final Reactor reactor, final int delay, final DispatchHandler timerCallback) {
this.delay = delay;
this.timerCallback = timerCallback;
this.reactor = reactor;
}
@Override
public void onTimerTask(Event e) {
this.reactor.schedule(this.delay, this.timerCallback);
}
}
private final class ScheduleHandler implements Callback {
@Override
public void run(Selectable selectable) {
try {
ioSignal.source().read(ByteBuffer.allocate(1024));
} catch (ClosedChannelException ignorePipeClosedDuringReactorShutdown) {
} catch (IOException ioException) {
throw new RuntimeException(ioException);
}
BaseHandler topWork;
while ((topWork = workQueue.poll()) != null) {
topWork.onTimerTask(null);
}
}
}
private final class CloseHandler implements Callback {
@Override
public void run(Selectable selectable) {
try {
selectable.getChannel().close();
} catch (IOException ignore) { }
try {
if (ioSignal.sink().isOpen()) {
ioSignal.sink().close();
}
} catch (IOException ignore) { }
workScheduler.run(null);
try {
if (ioSignal.source().isOpen()) {
ioSignal.source().close();
}
} catch (IOException ignore) { }
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy