
org.zeromq.ZMonitor Maven / Gradle / Ivy
package org.zeromq;
import java.io.Closeable;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import org.zeromq.ZMQ.Socket;
import org.zeromq.proto.ZNeedle;
import zmq.util.Objects;
/**
* The ZMonitor actor provides an API for obtaining socket events such as
* connected, listen, disconnected, etc. Socket events are only available
* for sockets connecting or bound to ipc:// and tcp:// endpoints.
*/
public class ZMonitor implements Closeable
{
/**
* High-level representation of an event.
*/
public static final class ZEvent
{
// Enum.values return a different array on each call, so keep it to avoid useless allocations
private static final Event[] EVENTVALUES = Event.values();
/**
* The type of the event. (never null)
*/
public final Event type;
/**
* The numerical code of the event. (useful in case of unrecognized event, in which case type is ALL)
*/
public final int code;
/**
* The address of the source of the event.
*/
public final String address;
/**
* String representation of the event value.
* Nature of the value depends on the type of event. May be null
*/
public final String value;
private ZEvent(ZMsg msg)
{
assert (msg != null);
assert (msg.size() == 1 || msg.size() == 2) : msg.size();
final ZFrame frame = msg.pop();
final ZNeedle needle = new ZNeedle(frame);
type = EVENTVALUES[needle.getNumber1()];
code = needle.getNumber4();
address = needle.getString();
if (msg.isEmpty()) {
value = null;
}
else {
value = msg.popString();
}
}
@SuppressWarnings("deprecation")
public ZEvent(ZMQ.Event event)
{
code = event.getEvent();
address = event.getAddress();
type = Event.findByCode(code);
Object tryValue = event.resolveValue();
if (tryValue != null) {
value = tryValue.toString();
}
else {
value = null;
}
}
@Override
public String toString()
{
return "ZEvent [type=" + type + ", code=" + code + ", address=" + address + ", value=" + value + "]";
}
}
/**
* Enumerates types of monitoring events.
*/
public enum Event
{
CONNECTED(ZMQ.EVENT_CONNECTED),
CONNECT_DELAYED(ZMQ.EVENT_CONNECT_DELAYED),
CONNECT_RETRIED(ZMQ.EVENT_CONNECT_RETRIED),
LISTENING(ZMQ.EVENT_LISTENING),
BIND_FAILED(ZMQ.EVENT_BIND_FAILED),
ACCEPTED(ZMQ.EVENT_ACCEPTED),
ACCEPT_FAILED(ZMQ.EVENT_ACCEPT_FAILED),
CLOSED(ZMQ.EVENT_CLOSED),
CLOSE_FAILED(ZMQ.EVENT_CLOSE_FAILED),
DISCONNECTED(ZMQ.EVENT_DISCONNECTED),
MONITOR_STOPPED(ZMQ.EVENT_MONITOR_STOPPED),
HANDSHAKE_FAILED_NO_DETAIL(ZMQ.HANDSHAKE_FAILED_NO_DETAIL),
HANDSHAKE_SUCCEEDED(ZMQ.HANDSHAKE_SUCCEEDED),
HANDSHAKE_FAILED_PROTOCOL(ZMQ.HANDSHAKE_FAILED_PROTOCOL),
HANDSHAKE_FAILED_AUTH(ZMQ.HANDSHAKE_FAILED_AUTH),
HANDSHAKE_PROTOCOL(ZMQ.EVENT_HANDSHAKE_PROTOCOL),
ALL(ZMQ.EVENT_ALL);
private static final Map MAP = new HashMap<>(Event.values().length);
static {
for (Event e : Event.values()) {
MAP.put(e.code, e);
}
}
private final int code;
Event(int code)
{
this.code = code;
}
/**
* Find the {@link Event} associated with the numerical event code.
* @param event the numerical event code
* @return the found {@link Event}
*/
public static Event findByCode(int event)
{
return MAP.getOrDefault(event, ALL);
}
}
/**
* The code returned by handshake events, as generated by eventHandshakeXXX
.
*
*/
public enum ProtocolCode
{
ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED),
ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND),
ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE),
ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY),
ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME),
ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA),
ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC),
ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH),
ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED),
ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY),
ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID),
ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION),
ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE),
ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA),
ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED(zmq.ZMQ.ZMQ_PROTOCOL_ERROR_WS_UNSPECIFIED);
private static final Map MAP = new HashMap<>(ProtocolCode.values().length);
static {
for (ProtocolCode e : ProtocolCode.values()) {
MAP.put(e.code, e);
}
}
private final int code;
ProtocolCode(int code)
{
this.code = code;
}
/**
* Find the {@link ProtocolCode} associated with the numerical error code send with eventHandshakeXXX
.
* @param code the numerical error code
* @return the found {@link ProtocolCode}
*/
public static ProtocolCode findByCode(int code)
{
if (MAP.containsKey(code)) {
return MAP.get(code);
}
else {
throw new IllegalArgumentException("Protocol code unknown: " + code);
}
}
}
private boolean started;
/**
* Starts the monitoring.
* Event types have to be added before the start, or they will take no effect.
* @return this instance.
*/
public final ZMonitor start()
{
if (started) {
System.out.println("ZMonitor: Unable to start while already started.");
return this;
}
agent.send(START);
agent.recv();
started = true;
return this;
}
/**
* Stops the monitoring and closes the actor.
* When returning from that call, ZMonitor will be no more active.
*/
@Override
public final void close()
{
destroy();
}
/**
* Stops the monitoring and closes the actor.
* When returning from that call, ZMonitor will be no more active.
*/
public final void destroy()
{
agent.send(CLOSE);
exit.awaitSilent();
agent.close();
}
/**
* Sets verbosity of the monitor.
* @param verbose true for monitor to be verbose, otherwise false.
* @return this instance.
*/
public final ZMonitor verbose(boolean verbose)
{
if (started) {
System.out.println("ZMonitor: Unable to change verbosity while already started.");
return this;
}
agent.send(VERBOSE, true);
agent.send(Boolean.toString(verbose));
agent.recv();
return this;
}
/**
* Adds event types to monitor.
* @param events the types of events to monitor.
* @return this instance.
*/
public final ZMonitor add(Event... events)
{
if (started) {
System.out.println("ZMonitor: Unable to add events while already started.");
return this;
}
ZMsg msg = new ZMsg();
msg.add(ADD_EVENTS);
for (Event evt : events) {
msg.add(evt.name());
}
agent.send(msg);
agent.recv();
return this;
}
/**
* Removes event types from monitor.
* @param events the types of events to stop monitoring.
* @return this instance.
*/
public final ZMonitor remove(Event... events)
{
if (started) {
System.out.println("ZMonitor: Unable to remove events while already started.");
return this;
}
ZMsg msg = new ZMsg();
msg.add(REMOVE_EVENTS);
for (Event evt : events) {
msg.add(evt.name());
}
agent.send(msg);
agent.recv();
return this;
}
/**
* Gets the next event, blocking for it until available.
* @return the next monitored event or null if closed.
*/
public final ZEvent nextEvent()
{
return nextEvent(true);
}
/**
* Gets the next event, blocking for it until available if requested.
* @param wait true to block until next event is available, false to immediately return with null if there is no event.
* @return the next event or null if there is currently none.
*/
public final ZEvent nextEvent(boolean wait)
{
if (!started) {
System.out.println("ZMonitor: Start before getting events.");
return null;
}
ZMsg msg = agent.recv(wait);
if (msg == null) {
return null;
}
return new ZEvent(msg);
}
/**
* Gets the next event, blocking for it until available if requested.
* @param timeout the time in milliseconds to wait for a message before returning null, -1 to block.
* @return the next event or null if there is currently none after the specified timeout.
*/
public final ZEvent nextEvent(int timeout)
{
if (!started) {
System.out.println("ZMonitor: Start before getting events.");
return null;
}
ZMsg msg = agent.recv(timeout);
if (msg == null) {
return null;
}
return new ZEvent(msg);
}
private static final String START = "START";
private static final String CLOSE = "CLOSE";
private static final String VERBOSE = "VERBOSE";
private static final String ADD_EVENTS = "ADD_EVENTS";
private static final String REMOVE_EVENTS = "REMOVE_EVENTS";
private final ZAgent agent;
private final ZStar.Exit exit;
/**
* Creates a monitoring actor for the given socket.
* @param ctx the context relative to this actor. Not null.
* @param socket the socket to monitor for events. Not null.
*/
public ZMonitor(ZContext ctx, Socket socket)
{
Objects.requireNonNull(ctx, "ZMonitor works only with a supplied context");
Objects.requireNonNull(socket, "Socket has to be supplied");
final MonitorActor actor = new MonitorActor(socket);
final ZActor zactor = new ZActor(ctx, actor, UUID.randomUUID().toString());
agent = zactor.agent();
exit = zactor.exit();
// wait for the start of the actor
agent.recv().destroy();
}
private static class MonitorActor extends ZActor.SimpleActor
{
private static final String ERROR = "ERROR";
private static final String OK = "OK";
private final Socket monitored; // the monitored socket
private final String address; // the address where events will be collected
private Socket monitor; // the monitoring socket
private int events; // the events to monitor
private boolean verbose;
public MonitorActor(ZMQ.Socket socket)
{
assert (socket != null);
this.monitored = socket;
this.address = String.format("inproc://zmonitor-%s-%s", socket.hashCode(), UUID.randomUUID());
}
@Override
public String premiere(Socket pipe)
{
return "ZMonitor-" + monitored.toString();
}
@Override
public List createSockets(ZContext ctx, Object... args)
{
monitor = ctx.createSocket(SocketType.PAIR);
assert (monitor != null);
return Collections.singletonList(monitor);
}
@Override
public void start(Socket pipe, List sockets, ZPoller poller)
{
pipe.send("STARTED");
}
@Override
public boolean stage(Socket socket, Socket pipe, ZPoller poller, int evts)
{
final zmq.ZMQ.Event event = zmq.ZMQ.Event.read(socket.base());
assert (event != null);
final int code = event.event;
final String address = event.addr;
assert (address != null);
final Event type = Event.findByCode(code);
assert (type != null);
final ZMsg msg = new ZMsg();
final ZFrame frame = new ZFrame(new byte[1 + 4 + address.length() + 4]);
final ZNeedle needle = new ZNeedle(frame);
needle.putNumber1(type.ordinal());
needle.putNumber4(code);
needle.putString(address);
msg.add(frame);
final Object value = event.arg;
if (value != null) {
msg.add(value.toString());
}
return msg.send(pipe, true);
}
@Override
public boolean backstage(ZMQ.Socket pipe, ZPoller poller, int evts)
{
final String command = pipe.recvStr();
if (command == null) {
System.out.printf("ZMonitor: Closing monitor %s : No command%n", monitored);
return false;
}
switch (command) {
case VERBOSE:
verbose = Boolean.parseBoolean(pipe.recvStr());
return pipe.send(OK);
case ADD_EVENTS:
return addEvents(pipe);
case REMOVE_EVENTS:
return removeEvents(pipe);
case START:
return start(poller, pipe);
case CLOSE:
return close(poller, pipe);
default:
System.out.printf("ZMonitor: Closing monitor %s : Unknown command %s%n", monitored, command);
pipe.send(ERROR);
return false;
}
}
private boolean addEvents(Socket pipe)
{
final ZMsg msg = ZMsg.recvMsg(pipe);
if (msg == null) {
return false; // interrupted
}
for (ZFrame frame : msg) {
final String evt = frame.getString(ZMQ.CHARSET);
final Event event = Event.valueOf(evt);
if (verbose) {
System.out.printf("ZMonitor: Adding" + " event %s%n", event);
}
events |= event.code;
}
return pipe.send(OK);
}
private boolean removeEvents(Socket pipe)
{
final ZMsg msg = ZMsg.recvMsg(pipe);
if (msg == null) {
return false; // interrupted
}
for (ZFrame frame : msg) {
final String evt = frame.getString(ZMQ.CHARSET);
final Event event = Event.valueOf(evt);
if (verbose) {
System.out.printf("ZMonitor: Removing" + " event %s%n", event);
}
events &= ~event.code;
}
return pipe.send(OK);
}
private boolean start(ZPoller poller, Socket pipe)
{
boolean rc = true;
String err = "";
if (rc) {
rc = monitored.monitor(address, events);
err = "Unable to monitor socket " + monitored;
}
if (rc) {
err = "Unable to connect monitoring socket " + monitor;
rc = monitor.connect(address);
}
if (rc) {
err = "Unable to poll monitoring socket " + monitor;
rc = poller.register(monitor, ZPoller.IN);
}
log("tart", rc, err);
if (rc) {
return pipe.send(OK);
}
pipe.send(ERROR);
return false;
}
private boolean close(ZPoller poller, Socket pipe)
{
boolean rc = poller.unregister(monitor);
String err = "Unable to unregister monitoring socket " + monitor;
if (rc) {
err = "Unable to stop monitor socket " + monitored;
rc = monitored.monitor(null, events);
}
log("top", rc, err);
if (verbose) {
System.out.printf("ZMonitor: Closing monitor %s%n", monitored);
}
pipe.send(rc ? OK : ERROR);
return false;
}
private void log(String action, boolean rc, String err)
{
if (verbose) {
if (rc) {
System.out.printf("ZMonitor: S%s monitor for events %s on %s%n", action, events, monitored);
}
else {
System.out.printf(
"ZMonitor: Unable to s%s monitor for events %s (%s) on %s%n",
action,
events,
err,
monitored);
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy