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<Socket> createSockets(ZContext ctx, Object... args)
        {
            monitor = ctx.createSocket(SocketType.PAIR);
            assert (monitor != null);

            return Collections.singletonList(monitor);
        }

        @Override
        public void start(Socket pipe, List<Socket> sockets, ZPoller poller)
        {
            pipe.send("STARTED");
        }

        @Override
        public boolean stage(Socket socket, Socket pipe, ZPoller poller, int evts)
        {<FILL_FUNCTION_BODY>}

        @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);
                }
            }
        }
    }

            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);
        
<no_super_class>