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. Please note, a newer package com.azure:azure-messaging-servicebus for Azure Service Bus is available as of December 2020. While this package will continue to receive critical bug fixes, we strongly encourage you to upgrade. Read the migration guide at https://aka.ms/azsdk/java/migrate/sb for more details.
/*
* Copyright (c) Microsoft. All rights reserved.
* Licensed under the MIT license. See LICENSE file in the project root for full license information.
*/
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 final class DelayHandler extends BaseHandler
{
final int delay;
final BaseHandler timerCallback;
final Reactor reactor;
public 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)
{
}
}
}
}