1 package org.evergreen_ils.hatch;
3 import java.io.IOException;
5 import java.io.BufferedReader;
6 import org.eclipse.jetty.websocket.api.Session;
7 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
8 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
9 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
10 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
11 import org.eclipse.jetty.websocket.api.annotations.WebSocket;
12 import javax.servlet.ServletConfig;
14 import org.eclipse.jetty.util.ajax.JSON;
15 import org.eclipse.jetty.util.log.Log;
16 import org.eclipse.jetty.util.log.Logger;
17 import java.util.Arrays;
18 import java.util.List;
19 import java.util.HashMap;
23 public class HatchWebSocketHandler {
25 private Session session;
26 private static String[] trustedDomains;
27 private static boolean trustAllDomains = false;
28 private static String profileDirectory;
29 private static final Logger logger = Log.getLogger("WebSocketHandler");
31 public static void setTrustedDomains(String[] domains) {
32 trustedDomains = domains;
34 if (domains.length > 0 ) {
36 if ("*".equals(domains[0])) {
37 logger.info("All domains trusted");
38 trustAllDomains = true;
42 for(String domain : trustedDomains) {
43 logger.info("Trusted domain: " + domain);
47 logger.warn("No domains are trusted");
51 public static void setProfileDirectory(String directory) {
52 profileDirectory = directory;
57 * config is passed in from our WebSocketServlet container,
58 * hence the public+static. Possible to access directly?
60 //public static void configure(ServletConfig config) {
61 public static void configure() {
62 logger.info("WebSocketHandler.configure()");
64 // default to ~/.evergreen
65 if (profileDirectory == null) {
66 String home = System.getProperty("user.home");
67 profileDirectory = new File(home, ".evergreen").getPath();
68 if (profileDirectory == null) {
69 logger.info("Unable to set profile directory");
74 protected boolean verifyOriginDomain() {
75 logger.info("received connection from IP " +
76 session.getRemoteAddress().getAddress());
78 String origin = session.getUpgradeRequest().getHeader("Origin");
81 logger.warn("No Origin header in request; Dropping connection");
85 logger.info("connection origin is " + origin);
87 if (trustAllDomains) return true;
89 if (java.util.Arrays.asList(trustedDomains).indexOf(origin) < 0) {
90 logger.warn("Request from un-trusted domain: " + origin);
99 public void onConnect(Session session) {
100 this.session = session;
101 if (!verifyOriginDomain()) session.close();
105 public void onClose(int statusCode, String reason) {
106 logger.info("onClose() statusCode=" + statusCode + ", reason=" + reason);
110 private void reply(Object json, String msgid) {
111 reply(json, msgid, true);
114 private void reply(Object json, String msgid, boolean success) {
116 Map<String, Object> response = new HashMap<String, Object>();
117 response.put("msgid", msgid);
119 response.put("success", json);
121 response.put("error", json);
125 String jsonString = JSON.toString(response);
126 if (!success) logger.warn(jsonString);
127 session.getRemote().sendString(jsonString);
128 } catch (IOException e) {
134 @SuppressWarnings("unchecked") // direct casting JSON-parsed objects
135 public void onMessage(String message) {
136 if (session == null || !session.isOpen()) return;
137 logger.info("onMessage() " + message);
139 HashMap<String,String> params = null;
142 params = (HashMap<String,String>) JSON.parse(message);
143 } catch (ClassCastException e) {
144 reply("Invalid WebSockets JSON message " + message, "", false);
148 String msgid = params.get("msgid");
149 String action = params.get("action");
150 String key = params.get("key");
151 String value = params.get("value");
152 String mime = params.get("mime");
154 // all requets require a message ID
155 if (msgid == null || msgid.equals("")) {
156 reply("No msgid specified in request", msgid, false);
160 // all requests require an action
161 if (action == null || action.equals("")) {
162 reply("No action specified in request", msgid, false);
166 if (action.equals("keys")) {
167 io = new FileIO(profileDirectory);
168 String[] keys = io.keys(key); // OK for key to be null
172 reply("key lookup error", msgid, false);
177 if (action.equals("printers")) {
178 List printers = new PrintManager().getPrinters();
179 reply(printers, msgid);
183 if (action.equals("print")) {
184 // TODO: validate the print target first so we can respond
185 // with an error if the requested printer / attributes are
186 // not supported. Printing occurs in a separate thread,
187 // so for now just assume it succeeded. Maybe later add
188 // a response queue and see if this handler is capable of
189 // responding from an alternate thread.
190 Hatch.enqueueMessage(params);
191 reply("print succeeded", msgid);
195 // all remaining requests require a key
196 if (key == null || key.equals("")) {
197 reply("No key specified in request", msgid, false);
201 if (action.equals("get")) {
202 io = new FileIO(profileDirectory);
203 BufferedReader reader = io.get(key);
204 if (reader != null) {
207 while ( (line = reader.readLine()) != null) {
208 // relay lines of text to the caller as we read them
209 // assume the text content is JSON and return it
213 } catch (IOException e) {
217 reply("Error accessing property " + key, msgid, false);
222 if (action.equals("delete")) {
223 io = new FileIO(profileDirectory);
224 if (io.delete(key)) {
225 reply("Delete of " + key + " successful", msgid);
227 reply("Delete of " + key + " failed", msgid, false);
232 // all remaining actions require value
234 reply("No value specified in request", msgid, false);
241 io = new FileIO(profileDirectory);
242 if (io.set(key, value)) {
243 reply("setting value for " + key + " succeeded", msgid);
245 reply("setting value for " + key + " succeeded", msgid, false);
250 io = new FileIO(profileDirectory);
251 if (io.append(key, value)) {
252 reply("appending value for " + key + " succeeded", msgid);
254 reply("appending value for " + key + " succeeded", msgid, false);
259 reply("No such action: " + action, msgid, false);