class State
        {
            @Override
            public String toString()
            {
                return "State [alive=" + alive + ", started=" + started + ", paused=" + paused + ", restart=" + restart
                        + ", hot=" + hot + "]";
            }

            // are we alive ?
            private boolean alive = false;
            // are we started ?
            private boolean started = false;
            // are we paused ?
            private boolean paused = false;

            // controls creation of a new agent if asked of a cold restart
            private boolean restart = false;
            // one-shot configuration for hot restart
            private ZMsg hot;
        }

        // the state of the proxy
        private final State state = new State();

        // used to transfer message from one socket to another
        private final Pump transport;

        // the nice name of the proxy
        private final String name;

        // the provider of the sockets
        private Proxy provider;

        // the sockets creator user arguments
        private Object[] args;

        private Socket frontend;
        private Socket backend;
        private Socket capture;

        // creates a new Proxy actor.
        public ProxyActor(String name, Pump transport, int id)
        {
            if (name == null) {
                // default basic name
                this.name = String.format("ZProxy-%sd", id);
            }
            else {
                this.name = name;
            }
            this.transport = transport;
        }

        @Override
        public String premiere(Socket pipe)
        {
            ZMsg reply = new ZMsg();
            reply.add(ALIVE);
            reply.send(pipe);

            return name;
        }

        // creates the sockets before the start of the proxy
        @Override
        public List<Socket> createSockets(ZContext ctx, Object... args)
        {
            provider = (Proxy) args[0];

            this.args = new Object[args.length - 1];
            System.arraycopy(args, 1, this.args, 0, this.args.length);

            frontend = provider.create(ctx, Plug.FRONT, this.args);
            capture = provider.create(ctx, Plug.CAPTURE, this.args);
            backend = provider.create(ctx, Plug.BACK, this.args);

            assert (frontend != null);
            assert (backend != null);

            return Arrays.asList(frontend, backend);
        }

        @Override
        public void start(Socket pipe, List<Socket> sockets, ZPoller poller)
        {
            // init the state machine
            state.alive = true;
            state.restart = false;
        }

        // Process a control message
        @Override
        public boolean backstage(Socket pipe, ZPoller poller, int events)
        {
            assert (state.hot == null);

            String cmd = pipe.recvStr();
            // a message has been received from the API
            if (START.equals(cmd)) {
                if (start(poller)) {
                    return status().send(pipe);
                }
                // unable to start the proxy, exit
                state.restart = false;
                return false;
            }
            else if (STOP.equals(cmd)) {
                stop();
                return status().send(pipe);
            }
            else if (PAUSE.equals(cmd)) {
                pause(poller, true);
                return status().send(pipe);
            }
            else if (RESTART.equals(cmd)) {
                String val = pipe.recvStr();
                boolean hot = Boolean.parseBoolean(val);
                return restart(pipe, hot);
            }
            else if (STATUS.equals(cmd)) {
                return status().send(pipe);
            }
            else if (CONFIG.equals(cmd)) {
                ZMsg cfg = ZMsg.recvMsg(pipe);
                boolean rc = provider.configure(pipe, cfg, frontend, backend, capture, args);
                cfg.destroy();
                return rc;
            }
            else if (EXIT.equals(cmd)) {
                // stops the proxy and the agent.
                // the status will be sent at the end of the loop
                state.restart = false;
            }
            else {
                return provider.custom(pipe, cmd, frontend, backend, capture, args);
            }
            return false;
        }

        // returns the status
        private ZMsg status()
        {
            ZMsg reply = new ZMsg();
            if (!state.alive) {
                reply.add(EXITED);
            }
            else if (state.paused) {
                reply.add(PAUSED);
            }
            else if (state.started) {
                reply.add(STARTED);
            }
            else {
                reply.add(STOPPED);
            }

            return reply;
        }

        // starts the proxy sockets
        private boolean start(ZPoller poller)
        {<FILL_FUNCTION_BODY>

            boolean success = true;
            if (!state.started) {
                try {
                    success = false;
                    success |= provider.configure(frontend, Plug.FRONT, args);
                    success |= provider.configure(backend, Plug.BACK, args);
                    success |= provider.configure(capture, Plug.CAPTURE, args);
                    state.started = true;
                }
                catch (RuntimeException | IOException e) {
                    e.printStackTrace();
                    // unable to configure proxy, exit
                    state.restart = false;
                    state.started = false;
                    return false;
                }
            }
            pause(poller, false);
            return success;
        
<no_super_class>