org.zeromq.ZPoller Maven / Gradle / Ivy
Show all versions of jeromq Show documentation
package org.zeromq;
import java.io.Closeable;
import java.io.IOException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.Selector;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.zeromq.ZMQ.Poller;
import org.zeromq.ZMQ.Socket;
/**
* Rewritten poller for 0MQ.
*
* Polls selectable channels and sockets for specified events.
*
* This poller can be used in two ways:
*
*
* - the traditional one, where you make something like
*
* {@code
ZPoller poller = ...
poller.register(socket, ZPoller.POLLIN);
poller.register(channel, ZPoller.OUT);
int events = poller.poll(-1L);
if (poller.isReadable(socket)) {
...
}
if (poller.writable(channel)) {
...
}
}
* - the event-driven way
*
* {@code
*
ZPoller poller = ...
poller.setGlobalHandler(...)
ZPoller.EventsHandler handler = ...
// The events method of the handler will be called
poller.register(channel, handler, ZPoller.IN);
// The events method of the global handler will be called
poller.register(socket, ZPoller.POLLOUT);
poller.poll(-1L);
// handlers have been called
}
* The motivations of this rewriting are:
*
* - the bare poller used zmq.ZMQ#poll(zmq.poll.PollItem[], int, long). This method did not allow
* to choose the selector used for polling, relying on a ThreadLocal, which is dangerous.
*
* - the bare poller use algorithms tailored for languages with manual allocation.
* No need here as Java allows more flexibility. TODO There still may be a small penalty cost.
*
*/
// Poller for 0MQ.
public class ZPoller implements Closeable
{
// contract for events
public interface EventsHandler
{
/**
* Called when the poller intercepts events.
*
* @param socket the socket with events
* @param events the interesting events as an ORed combination of IN, OUT, ERR
* @return true to continue the polling, false to stop it
*/
boolean events(Socket socket, int events);
/**
* Called when the poller intercepts events.
*
* @param channel the channel with events
* @param events the interesting events as an ORed combination of IN, OUT, ERR
* @return true to continue the polling, false to stop it
*/
boolean events(SelectableChannel channel, int events);
}
// contract for items. Useful for providing own implementation.
public interface ItemHolder
{
// the inner ZMQ poll item
zmq.poll.PollItem item();
// the related ZeroMQ socket
Socket socket();
// the associated events handler
EventsHandler handler();
}
// contract for items creation. Useful for delegating.
public interface ItemCreator
{
/**
* Creates a new holder for a poll item.
*
* @param socket the socket to poll
* @param handler the optional handler for polled events
* @param events the interested events
* @return a new poll item holder
*/
ItemHolder create(Socket socket, EventsHandler handler, int events);
/**
* Creates a new holder for a poll item.
*
* @param channel the channel to poll
* @param handler the optional handler for polled events.
* @param events the interested events
* @return a new poll item holder
*/
ItemHolder create(SelectableChannel channel, EventsHandler handler, int events);
}
// re-re-implementation of poll item
public static class ZPollItem extends ZMQ.PollItem implements ItemHolder
{
private final EventsHandler handler;
public ZPollItem(final Socket socket, final EventsHandler handler, int ops)
{
super(socket, ops);
this.handler = handler;
assert (item() != null);
}
public ZPollItem(final SelectableChannel channel, final EventsHandler handler, final int ops)
{
super(channel, ops);
this.handler = handler;
assert (item() != null);
}
@Override
public zmq.poll.PollItem item()
{
return base();
}
@Override
public Socket socket()
{
return getSocket();
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((item() == null) ? 0 : item().hashCode());
result = prime * result + ((getRawSocket() == null) ? 0 : getRawSocket().hashCode());
result = prime * result + ((socket() == null) ? 0 : socket().hashCode());
result = prime * result + ((handler() == null) ? 0 : handler().hashCode());
return result;
}
@Override
public boolean equals(final Object obj)
{
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ItemHolder)) {
return false;
}
ItemHolder other = (ItemHolder) obj;
// this.item() is never null
if (other.item() == null) {
return false;
}
// no checking on item() as zmq.PollItem does not override equals()
if (item().getRawSocket() == null) {
if (other.item().getRawSocket() != null) {
return false;
}
}
else if (!item().getRawSocket().equals(other.item().getRawSocket())) {
return false;
}
if (socket() == null) {
if (other.socket() != null) {
return false;
}
}
else if (!socket().equals(other.socket())) {
return false;
}
if (handler() == null) {
if (other.handler() != null) {
return false;
}
}
else if (!handler().equals(other.handler())) {
return false;
}
return true;
}
@Override
public EventsHandler handler()
{
return handler;
}
}
/******************************************************************************/
/* 0MQ socket events */
/******************************************************************************/
/*
* These values can be ORed to specify what we want to poll for.
*/
public static final int POLLIN = Poller.POLLIN;
public static final int POLLOUT = Poller.POLLOUT;
public static final int POLLERR = Poller.POLLERR;
// same values with shorter writing
public static final int IN = POLLIN;
public static final int OUT = POLLOUT;
public static final int ERR = POLLERR;
// same values with semantic consistency
public static final int READABLE = POLLIN;
public static final int WRITABLE = POLLOUT;
/**
* Creates a new poller based on the current one.
* This will be a shadow poller, sharing the same selector and items creator.
* The global events handler will not be shared.
*
* @param poller the main poller.
*/
public ZPoller(final ZPoller poller)
{
this(poller.creator, poller.selector);
}
/**
* Creates a new poller with a given selector for operational polling.
*
* @param selector the selector to use for polling.
*/
public ZPoller(final Selector selector)
{
this(new SimpleCreator(), selector);
}
/**
* Creates a new poller attached to a given context that will provide
* selector for operational polling.
*
* @param context
* the context that will provide the selector to use for polling.
*/
public ZPoller(final ZContext context)
{
this(new SimpleCreator(), context.createSelector());
}
/**
* Creates a new poller based on the current one. This will be a shadow
* poller, sharing the same selector. The global events handler will not be
* shared.
*
* @param creator the items creator
* @param poller the main poller.
*/
public ZPoller(final ItemCreator creator, final ZPoller poller)
{
this(creator, poller.selector);
}
/**
* Creates a new poller attached to a given context that will provide
* selector for operational polling.
*
* @param creator
* the items creator
* @param context
* the context that will provide the selector to use for polling.
*/
public ZPoller(final ItemCreator creator, final ZContext context)
{
this(creator, context.createSelector());
}
/**
* Creates a new poller.
*
* @param creator the items creator
* @param selector the selector to use for polling.
*/
public ZPoller(final ItemCreator creator, final Selector selector)
{
this.creator = creator;
this.selector = selector;
items = new HashMap<>();
all = createContainer(0);
}
// creates a new poll item
protected ItemHolder create(final Socket socket, final EventsHandler handler, final int events)
{
assert (socket != null);
return creator.create(socket, handler, events);
}
// creates a new poll item
protected ItemHolder create(final SelectableChannel channel, final EventsHandler handler, final int events)
{
assert (channel != null);
return creator.create(channel, handler, events);
}
/**
* Sets the global events handler for all registered sockets.
*
* @param globalHandler the events handler to set
*/
public void setGlobalHandler(final EventsHandler globalHandler)
{
this.globalHandler = globalHandler;
}
/**
* Returns the global events handler for all registered sockets.
*
* @return the global events handler for all registered sockets.
*/
public EventsHandler getGlobalHandler()
{
return globalHandler;
}
/**
* Register a Socket for polling on specified events.
*
* @param socket the registering socket.
* @param handler the events handler for this socket
* @param events the events to listen to, as a mask composed by ORing POLLIN, POLLOUT and POLLERR.
* @return true if registered, otherwise false
*/
public final boolean register(final Socket socket, final EventsHandler handler, final int events)
{
if (socket == null) {
return false;
}
return add(socket, create(socket, handler, events));
}
public final boolean register(final Socket socket, final EventsHandler handler)
{
return register(socket, handler, POLLIN | POLLOUT | POLLERR);
}
public final boolean register(final Socket socket, final int events)
{
return register(socket, globalHandler, events);
}
/**
* Register a SelectableChannel for polling on specified events.
*
* @param channel the registering channel.
* @param handler the events handler for this socket
* @param events the events to listen to, as a mask composed by XORing POLLIN, POLLOUT and POLLERR.
* @return true if registered, otherwise false
*/
public final boolean register(final SelectableChannel channel, final EventsHandler handler, final int events)
{
if (channel == null) {
return false;
}
return add(channel, create(channel, handler, events));
}
public final boolean register(final SelectableChannel channel, final EventsHandler handler)
{
return register(channel, handler, IN | OUT | ERR);
}
public final boolean register(final SelectableChannel channel, final int events)
{
return register(channel, globalHandler, events);
}
/**
* Register an ItemHolder for polling on specified events.
*
* @param item the registering item.
* @return true if registered, otherwise false
*/
public final boolean register(final ItemHolder item)
{
return add(null, item);
}
/**
* Unregister a Socket or SelectableChannel for polling on the specified events.
*
* @param socketOrChannel the Socket or SelectableChannel to be unregistered
* @return true if unregistered, otherwise false
* TODO would it be useful to unregister only for specific events ?
*/
public final boolean unregister(final Object socketOrChannel)
{
if (socketOrChannel == null) {
return false;
}
Set items = this.items.remove(socketOrChannel);
boolean rc = items != null;
if (rc) {
all.removeAll(items);
}
return rc;
}
/******************************************************************************/
/* POLLING | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |*/
/******************************************************************************/
/**
* Issue a poll call, using the specified timeout value.
*
* Since ZeroMQ 3.0, the timeout parameter is in milliseconds,
* but prior to this the unit was microseconds.
*
* @param timeout
* the timeout, as per zmq_poll ();
* if -1, it will block indefinitely until an event
* happens; if 0, it will return immediately;
* otherwise, it will wait for at most that many
* milliseconds/microseconds (see above).
* @see "http://api.zeromq.org/3-0:zmq-poll"
* @return how many objects where signaled by poll ()
*/
public int poll(final long timeout)
{
return poll(timeout, true);
}
/**
* Issue a poll call, using the specified timeout value.
*
* @param timeout the timeout, as per zmq_poll ();
* @param dispatchEvents true to dispatch events using items handler and the global one.
* @see "http://api.zeromq.org/3-0:zmq-poll"
* @return how many objects where signaled by poll ()
*/
protected int poll(final long timeout, final boolean dispatchEvents)
{
// get all the raw items
final Collection all = items();
final Set pollItems = new HashSet<>(all.size());
for (ItemHolder holder : all) {
pollItems.add(holder.item());
}
// polling time
final int rc = poll(selector, timeout, pollItems);
if (!dispatchEvents) {
// raw result
return rc;
}
if (dispatch(all, pollItems.size())) {
// returns event counts after dispatch if everything went fine
return rc;
}
// error in dispatching
return -1;
}
// does the effective polling
protected int poll(final Selector selector, final long tout, final Collection items)
{
final int size = items.size();
return zmq.ZMQ.poll(selector, items.toArray(new zmq.poll.PollItem[size]), size, tout);
}
/**
* Dispatches the polled events.
*
* @param all the items used for dispatching
* @param size the number of items to dispatch
* @return true if correctly dispatched, false in case of error
*/
protected boolean dispatch(final Collection all, int size)
{
ItemHolder[] array = all.toArray(new ItemHolder[all.size()]);
// protected against handlers unregistering during this loop
for (ItemHolder holder : array) {
EventsHandler handler = holder.handler();
if (handler == null) {
handler = globalHandler;
}
if (handler == null) {
// no handler, short-circuit
continue;
}
final zmq.poll.PollItem item = holder.item();
final int events = item.readyOps();
if (events <= 0) {
// no events, short-circuit
continue;
}
final Socket socket = holder.socket();
final SelectableChannel channel = holder.item().getRawSocket();
if (socket != null) {
assert (channel == null);
// dispatch on socket
if (!handler.events(socket, events)) {
return false;
}
}
if (channel != null) {
// dispatch on channel
assert (socket == null);
if (!handler.events(channel, events)) {
return false;
}
}
}
return true;
}
// dispatches all the polled events to their respective handlers
public boolean dispatch()
{
return dispatch(all, all.size());
}
/******************************************************************************/
/*| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | POLLING */
/******************************************************************************/
/**
* Tells if a channel is readable from this poller.
*
* @param channel the channel to ask for.
* @return true if readable, otherwise false
*/
public boolean isReadable(final SelectableChannel channel)
{
return readable((Object) channel);
}
public boolean readable(final SelectableChannel channel)
{
return readable((Object) channel);
}
/**
* Tells if a socket is readable from this poller.
*
* @param socket the socket to ask for.
* @return true if readable, otherwise false
*/
public boolean isReadable(final Socket socket)
{
return readable(socket);
}
public boolean readable(final Socket socket)
{
return readable((Object) socket);
}
// checks for read event
public boolean readable(final Object socketOrChannel)
{
final zmq.poll.PollItem it = filter(socketOrChannel, READABLE);
if (it == null) {
return false;
}
return it.isReadable();
}
public boolean pollin(final Socket socket)
{
return isReadable(socket);
}
public boolean pollin(final SelectableChannel channel)
{
return isReadable(channel);
}
/**
* Tells if a channel is writable from this poller.
*
* @param channel the channel to ask for.
* @return true if writable, otherwise false
*/
public boolean isWritable(final SelectableChannel channel)
{
return writable((Object) channel);
}
public boolean writable(final SelectableChannel channel)
{
return writable((Object) channel);
}
/**
* Tells if a socket is writable from this poller.
*
* @param socket the socket to ask for.
* @return true if writable, otherwise false
*/
public boolean isWritable(final Socket socket)
{
return writable((Object) socket);
}
public boolean writable(final Socket socket)
{
return writable((Object) socket);
}
// checks for write event
public boolean writable(final Object socketOrChannel)
{
final zmq.poll.PollItem it = filter(socketOrChannel, WRITABLE);
if (it == null) {
return false;
}
return it.isWritable();
}
public boolean pollout(final Socket socket)
{
return isWritable(socket);
}
public boolean pollout(final SelectableChannel channel)
{
return isWritable(channel);
}
/**
* Tells if a channel is in error from this poller.
*
* @param channel the channel to ask for.
* @return true if in error, otherwise false
*/
public boolean isError(final SelectableChannel channel)
{
return error((Object) channel);
}
public boolean error(final SelectableChannel channel)
{
return error((Object) channel);
}
/**
* Tells if a socket is in error from this poller.
*
* @param socket the socket to ask for.
* @return true if in error, otherwise false
*/
public boolean isError(final Socket socket)
{
return error((Object) socket);
}
public boolean error(final Socket socket)
{
return error((Object) socket);
}
// checks for error event
public boolean error(final Object socketOrChannel)
{
final zmq.poll.PollItem it = filter(socketOrChannel, ERR);
if (it == null) {
return false;
}
return it.isError();
}
public boolean pollerr(final Socket socket)
{
return isError(socket);
}
public boolean pollerr(final SelectableChannel channel)
{
return isError(channel);
}
/**
* Destroys the poller. Does actually nothing.
*/
@Override
public void close() throws IOException
{
destroy();
}
/**
* Destroys the poller and the associated selector without exception.
*/
public void destroy()
{
// we didn't create the selector,
// it is not our responsibility to close it.
}
// selector used for polling
private final Selector selector;
// creator of items
private final ItemCreator creator;
// managed items
private final Map