]> git.evergreen-ils.org Git - working/Hatch.git/blob - src/org/evergreen_ils/hatch/HatchWebSocketHandler.java
hatch : create job settings from config hash cont.
[working/Hatch.git] / src / org / evergreen_ils / hatch / HatchWebSocketHandler.java
1 /* -----------------------------------------------------------------------
2  * Copyright 2014 Equinox Software, Inc.
3  * Bill Erickson <berick@esilibrary.com>
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * -----------------------------------------------------------------------
15  */
16 package org.evergreen_ils.hatch;
17
18 import java.io.IOException;
19 import java.io.File;
20 import java.io.BufferedReader;
21 import org.eclipse.jetty.websocket.api.Session;
22 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
23 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
24 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
25 import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
26 import org.eclipse.jetty.websocket.api.annotations.WebSocket;
27 import javax.servlet.ServletConfig;
28
29 import org.eclipse.jetty.util.ajax.JSON;
30 import org.eclipse.jetty.util.log.Log;
31 import org.eclipse.jetty.util.log.Logger;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.HashMap;
35 import java.util.Map;
36
37 @WebSocket
38 public class HatchWebSocketHandler {
39
40     private Session session;
41     private static String[] trustedDomains;
42     private static boolean trustAllDomains = false;
43     private static String profileDirectory;
44     private static final Logger logger = Log.getLogger("WebSocketHandler");
45
46     public static void setTrustedDomains(String[] domains) {
47         trustedDomains = domains;
48
49         if (domains.length > 0 ) {
50
51             if ("*".equals(domains[0])) {
52                 logger.info("All domains trusted");
53                 trustAllDomains = true;
54
55             } else {
56
57                 for(String domain : trustedDomains) {
58                     logger.info("Trusted domain: " + domain);
59                 }
60             }
61         } else {
62             logger.warn("No domains are trusted");
63         }
64     }
65
66     public static void setProfileDirectory(String directory) {
67         profileDirectory = directory;
68     }
69
70
71     /**
72      * config is passed in from our WebSocketServlet container,
73      * hence the public+static.  Possible to access directly?
74      */
75     //public static void configure(ServletConfig config) {
76     public static void configure() {
77         logger.info("WebSocketHandler.configure()");
78
79         // default to ~/.evergreen
80         if (profileDirectory == null) {
81             String home = System.getProperty("user.home");
82             profileDirectory = new File(home, ".evergreen").getPath();
83             if (profileDirectory == null) {
84                 logger.info("Unable to set profile directory");
85             }
86         }
87     }
88
89     protected boolean verifyOriginDomain() {
90         logger.info("received connection from IP " +
91             session.getRemoteAddress().getAddress());
92
93         String origin = session.getUpgradeRequest().getHeader("Origin");
94
95         if (origin == null) {
96             logger.warn("No Origin header in request; Dropping connection");
97             return false;
98         }
99
100         logger.info("connection origin is " + origin);
101
102         if (trustAllDomains) return true;
103
104         if (java.util.Arrays.asList(trustedDomains).indexOf(origin) < 0) {
105             logger.warn("Request from un-trusted domain: " + origin);
106             return false;
107         }
108
109         return true;
110     }
111
112
113     @OnWebSocketConnect
114     public void onConnect(Session session) {
115         this.session = session;
116         if (!verifyOriginDomain()) session.close();
117     }
118
119     @OnWebSocketClose
120     public void onClose(int statusCode, String reason) {
121         logger.info("onClose() statusCode=" + statusCode + ", reason=" + reason);
122         this.session = null;
123     }
124
125     protected void reply(Object json, Long msgid) {
126         reply(json, msgid, true);
127     }
128
129     protected void reply(Object json, Long msgid, boolean success) {
130
131         Map<String, Object> response = new HashMap<String, Object>();
132         response.put("msgid", msgid);
133         if (success) {
134             response.put("content", json);
135         } else {
136             response.put("error", json);
137         }
138
139         logger.info("replying with : " + JSON.toString(response));
140
141         try {
142             String jsonString = JSON.toString(response);
143             if (!success) logger.warn(jsonString);
144             session.getRemote().sendString(jsonString);
145         } catch (IOException e) {
146             logger.warn(e);
147         }
148     }
149
150     @OnWebSocketMessage
151     @SuppressWarnings("unchecked") // direct casting JSON-parsed objects
152     public void onMessage(String message) {
153         if (session == null || !session.isOpen()) return;
154         logger.info("onMessage() " + message);
155
156         HashMap<String,Object> params = null;
157
158         try {
159             params = (HashMap<String,Object>) JSON.parse(message);
160         } catch (ClassCastException e) {
161             reply("Invalid WebSockets JSON message " + message, 
162                 new Long(-1), false);
163         }
164
165         FileIO io;
166         Long msgid = (Long) params.get("msgid");
167         String action = (String) params.get("action");
168         String key = (String) params.get("key");
169         String value = (String) params.get("value");
170         String mime = (String) params.get("mime");
171
172         logger.info("Received request for action " + action);
173
174         // all requets require a message ID
175         if (msgid == null) {
176             reply("No msgid specified in request", msgid, false);
177             return;
178         }
179
180         // all requests require an action
181         if (action == null || action.equals("")) {
182             reply("No action specified in request", msgid, false);
183             return;
184         }
185
186         if (action.equals("keys")) {
187             io = new FileIO(profileDirectory);
188             String[] keys = io.keys(key); // OK for key to be null
189             if (keys != null) {
190                 reply(keys, msgid);
191             } else {
192                 reply("key lookup error", msgid, false);
193             }
194             return;
195         }
196
197         if (action.equals("printers")) {
198             List printers = new PrintManager().getPrintersAsMaps();
199             reply(printers, msgid);
200             return;
201         }
202
203         if (action.equals("print")) {
204             // pass ourselves off to the print handler so it can reply
205             // for us after printing has completed.
206             params.put("socket", this);
207             Hatch.enqueueMessage(params);
208             return;
209         }
210
211         if (action.equals("print-config")) {
212             try {
213                 reply(
214                     new PrintManager().configurePrinter(params),
215                     msgid
216                 );
217             } catch(IllegalArgumentException e) {
218                 reply(e.toString(), msgid, false);
219             }
220             return;
221         }
222
223         // all remaining requests require a key
224         if (key == null || key.equals("")) {
225             reply("No key specified in request", msgid, false);
226             return;
227         }
228
229         if (action.equals("get")) {
230             String val = new FileIO(profileDirectory).get(key);
231             // set() calls store bare JSON. We must pass an 
232             // Object to reply so that it may be embedded into
233             // a larger JSON response object, hence the JSON.parse().
234             if (val == null) {
235                 reply(null, msgid);
236             } else {
237                 reply(JSON.parse(val), msgid);
238             }
239             return;
240         }
241
242         if (action.equals("remove")) {
243             io = new FileIO(profileDirectory);
244             if (io.delete(key)) {
245                 reply("Removal of " + key + " successful", msgid);
246             } else {
247                 reply("Removal of " + key + " failed", msgid, false);
248             }
249             return;
250         }
251
252         // all remaining actions require value
253         if (value == null) {
254             reply("No value specified in request", msgid, false);
255             return;
256         }
257
258         switch (action) {
259
260             case "set" :
261                 io = new FileIO(profileDirectory);
262                 if (io.set(key, value)) {
263                     reply("setting value for " + key + " succeeded", msgid);
264                 } else {
265                     reply("setting value for " + key + " succeeded", msgid, false);
266                 }
267                 break;
268
269             case "append" :
270                 io = new FileIO(profileDirectory);
271                 if (io.append(key, value)) {
272                     reply("appending value for " + key + " succeeded", msgid);
273                 } else {
274                     reply("appending value for " + key + " succeeded", msgid, false);
275                 }
276                 break;
277
278             default:
279                 reply("No such action: " + action, msgid, false);
280         }
281     }
282 }