001package jmri.web.servlet.simple;
002
003import java.io.IOException;
004import javax.servlet.annotation.WebServlet;
005import javax.servlet.http.HttpServlet;
006import javax.servlet.http.HttpServletRequest;
007import javax.servlet.http.HttpServletResponse;
008import jmri.InstanceManager;
009import jmri.JmriException;
010import jmri.jmris.JmriConnection;
011import jmri.jmris.simpleserver.SimpleLightServer;
012import jmri.jmris.simpleserver.SimpleOperationsServer;
013import jmri.jmris.simpleserver.SimplePowerServer;
014import jmri.jmris.simpleserver.SimpleReporterServer;
015import jmri.jmris.simpleserver.SimpleSensorServer;
016import jmri.jmris.simpleserver.SimpleSignalHeadServer;
017import jmri.jmris.simpleserver.SimpleTurnoutServer;
018import jmri.util.FileUtil;
019import jmri.util.node.NodeIdentity;
020import jmri.web.server.WebServerPreferences;
021import jmri.web.servlet.ServletUtil;
022import org.eclipse.jetty.websocket.api.Session;
023import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
024import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
025import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
026import org.eclipse.jetty.websocket.api.annotations.WebSocket;
027import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
028import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
029import org.openide.util.lookup.ServiceProvider;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * WebSocket servlet for JMRI Simple service protocol.
035 *
036 * @author Randall Wood (c) 2016
037 */
038@WebServlet(name = "SimpleServlet",
039        urlPatterns = {"/simple"})
040@ServiceProvider(service = HttpServlet.class)
041public class SimpleServlet extends WebSocketServlet {
042
043    private static final Logger log = LoggerFactory.getLogger(SimpleServlet.class);
044
045    @Override
046    public void configure(WebSocketServletFactory factory) {
047        factory.register(SimpleWebSocket.class);
048    }
049
050    @Override
051    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
052        response.setStatus(HttpServletResponse.SC_OK);
053        response.setContentType(ServletUtil.UTF8_TEXT_HTML); // NOI18N
054        response.getWriter().print(String.format(request.getLocale(),
055                FileUtil.readURL(FileUtil.findURL(Bundle.getMessage(request.getLocale(), "Simple.html"))),
056                String.format(request.getLocale(),
057                        Bundle.getMessage(request.getLocale(), "HtmlTitle"),
058                        InstanceManager.getDefault(ServletUtil.class).getRailroadName(false),
059                        Bundle.getMessage(request.getLocale(), "SimpleTitle")
060                ),
061                InstanceManager.getDefault(ServletUtil.class).getNavBar(request.getLocale(), request.getContextPath()),
062                InstanceManager.getDefault(ServletUtil.class).getRailroadName(false),
063                InstanceManager.getDefault(ServletUtil.class).getFooter(request.getLocale(), request.getContextPath())
064        ));
065    }
066
067    @WebSocket
068    public static class SimpleWebSocket {
069
070        private JmriConnection connection;
071        private Runnable shutDownTask;
072        private SimpleLightServer lightServer;
073        private SimpleOperationsServer operationsServer;
074        private SimplePowerServer powerServer;
075        private SimpleReporterServer reporterServer;
076        private SimpleSensorServer sensorServer;
077        private SimpleSignalHeadServer signalHeadServer;
078        private SimpleTurnoutServer turnoutServer;
079
080        public void sendMessage(String message) throws IOException {
081            this.connection.sendMessage(message);
082        }
083
084        @OnWebSocketConnect
085        public void onOpen(Session cnctn) {
086            this.connection = new JmriConnection(cnctn);
087            this.shutDownTask = () -> SimpleWebSocket.this.connection.getSession().close();
088            this.lightServer = new SimpleLightServer(this.connection);
089            this.operationsServer = new SimpleOperationsServer(this.connection);
090            this.powerServer = new SimplePowerServer(this.connection);
091            this.reporterServer = new SimpleReporterServer(this.connection);
092            this.sensorServer = new SimpleSensorServer(this.connection);
093            this.signalHeadServer = new SimpleSignalHeadServer(this.connection);
094            this.turnoutServer = new SimpleTurnoutServer(this.connection);
095            try {
096                this.connection.sendMessage("JMRI " + jmri.Version.name() + " \n");
097                this.connection.sendMessage("RAILROAD " + InstanceManager.getDefault(WebServerPreferences.class).getRailroadName() + " \n");
098                this.connection.sendMessage("NODE " + NodeIdentity.networkIdentity() + " \n");
099            } catch (IOException e) {
100                log.warn("Closing Session due to ", e);
101                this.connection.getSession().close();
102            }
103            InstanceManager.getDefault(jmri.ShutDownManager.class).register(this.shutDownTask);
104        }
105
106        @OnWebSocketError
107        public void onError(Throwable thrwbl) {
108            log.error("Socket Error: ", thrwbl);
109        }
110
111        @OnWebSocketMessage
112        public void onMessage(String string) {
113            log.debug("Received from client: {}", string);
114            try {
115                if (string.startsWith("POWER")) {
116                    this.powerServer.parseStatus(string);
117                    this.powerServer.sendStatus(InstanceManager.getDefault(jmri.PowerManager.class).getPower());
118                } else if (string.startsWith("TURNOUT")) {
119                    this.turnoutServer.parseStatus(string);
120                } else if (string.startsWith("LIGHT")) {
121                    this.lightServer.parseStatus(string);
122                } else if (string.startsWith("SENSOR")) {
123                    this.sensorServer.parseStatus(string);
124                } else if (string.startsWith("SIGNALHEAD")) {
125                    this.signalHeadServer.parseStatus(string);
126                } else if (string.startsWith("REPORTER")) {
127                    this.reporterServer.parseStatus(string);
128                } else if (string.startsWith(SimpleOperationsServer.OPERATIONS)) {
129                    this.operationsServer.parseStatus(string);
130                } else {
131                    this.connection.sendMessage("Unknown Command " + string + "\n");
132                }
133            } catch (JmriException je) {
134                try {
135                    this.connection.sendMessage("not supported\n");
136                } catch (IOException ie) {
137                    log.warn("Closing Connection due to Exception", ie);
138                    this.connection.getSession().close();
139                    InstanceManager.getDefault(jmri.ShutDownManager.class).deregister(this.shutDownTask);
140                }
141            } catch (IOException ie) {
142                log.warn("Closing Connection due to Exception", ie);
143                this.connection.getSession().close();
144                InstanceManager.getDefault(jmri.ShutDownManager.class).deregister(this.shutDownTask);
145            }
146        }
147    }
148}