]> git.evergreen-ils.org Git - working/Hatch.git/blob - src/org/evergreen_ils/hatch/RequestHandler.java
Native Messaging WIP - docs, tweaks
[working/Hatch.git] / src / org / evergreen_ils / hatch / RequestHandler.java
1 /* -----------------------------------------------------------------------
2  * Copyright 2016 King County Library System
3  * Bill Erickson <berickxx@gmail.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 org.json.*;
19 import java.io.File;
20 import java.util.logging.*;
21
22 /**
23  * Dispatches requests received via MessageIO, sends responses back 
24  * via MessageIO.
25  */
26 public class RequestHandler extends Thread {
27
28     /** STDIN/STDOUT handler */
29     private static MessageIO io = new MessageIO();
30
31     static final Logger logger = Hatch.getLogger();
32
33     /** Root directory for all FileIO operations */
34     private static String profileDirectory = null;
35
36     /** Origin host/domain used for segregating files by browser host */
37     private static String origin = null;
38
39     private void configure() {
40
41         // Find the profile directory.
42         // The profile directory + origin string represent the base 
43         // directory for all file I/O for this session.
44         if (profileDirectory == null) { // TODO: make configurable
45             String home = System.getProperty("user.home");
46             profileDirectory = new File(home, ".evergreen").getPath();
47             if (profileDirectory == null) {
48                 logger.warning("Unable to set profile directory");
49             }
50         }
51     }
52
53     /**
54      * Unpack a JSON request and send it to the necessary Hatch handler.
55      *
56      * @return True if the calling code should avoid calling reply() with
57      * the response object.
58      */
59     private boolean dispatchRequest(
60         JSONObject request, JSONObject response) throws JSONException {
61
62         String action = request.getString("action");
63
64         logger.info("Received message id=" + 
65             response.get("msgid") + " action=" + action);
66
67         // init must be called first to set the origin info
68         if (action.equals("init")) {
69             origin = request.getString("origin");
70             return false;
71         }
72
73         if (origin == null) {
74             response.put("status", 400); 
75             response.put("message", "'init' action must be called first!");
76             return false;
77         }
78
79         String key = null;
80         String content = null;
81         FileIO fileIO = new FileIO(profileDirectory, origin);
82
83         switch (action) {
84
85             case "printers":
86                 response.put("printers",
87                     new PrintManager().getPrintersAsMaps());
88                 break;
89
90             case "print":
91                 // Confirm a minimal data set to enqueue print requests.
92                 content = request.getString("content");
93                 String contentType = request.getString("contentType");
94
95                 if (content == null || "".equals(content)) {
96                     response.put("status", 400); 
97                     response.put("message", "Empty print message");
98
99                 } else {
100                     Hatch.enqueuePrintRequest(request);
101                     // Responses to print requests are generated asynchronously 
102                     // and delivered from the FX print thread via reply().
103                     return true;
104                 }
105
106             case "keys": // Return stored keys
107                 String pfxKey = request.optString("key");
108                 response.put("keys", fileIO.keys(pfxKey));
109                 break;
110             
111             case "get":
112                 key = request.getString("key");
113                 String val = fileIO.get(key);
114
115                 if (val != null) {
116                     // Translate the JSON string stored by set() into a
117                     // Java object that can be added to the response.
118                     Object jsonBlob = new JSONTokener(val).nextValue();
119                     response.put("content", jsonBlob);
120                 }
121                 break;
122
123             case "set" :
124                 key = request.getString("key");
125
126                 // JSON-ify the thing stored under "content"
127                 String json = JSONObject.valueToString(request.get("content"));
128
129                 if (!fileIO.set(key, json)) {
130                     response.put("status", 500);
131                     response.put("message", "Unable to set key: " + key);
132                 }
133                 break;
134
135             case "remove":
136                 key = request.getString("key");
137
138                 if (!fileIO.remove(key)) {
139                     response.put("status", 500);
140                     response.put("message", "Unable to remove key: " + key);
141                 }
142                 break;
143
144             default:
145                 response.put("status", 404); 
146                 response.put("message", "Action not found: " + action);
147         }
148
149         return false;
150     }
151
152     /**
153      * Most replies are delivered from within dispatchRequest, but some
154      * like printing require the reply be delivered from another thread.
155      */
156     protected static void reply(JSONObject response) {
157         io.sendMessage(response);
158     }
159
160     public void run() {
161
162         configure();
163         io.listen(); // STDIN/STDOUT handler
164
165         while (true) { 
166
167             boolean skipReply = false;
168             JSONObject response = new JSONObject();
169
170             // Status values overidden as needed by the dispatch handler.
171             response.put("status", 200); 
172             response.put("message", "OK");
173
174             try {
175                 JSONObject request = io.recvMessage();
176
177                 response.put("clientid", request.getLong("clientid"));
178                 response.put("msgid", request.getLong("msgid"));
179
180                 skipReply = dispatchRequest(request, response); 
181
182             } catch (JSONException je) {
183                 response.put("status", 400); 
184                 response.put("message", "Bad Request: " + je.toString());
185             }
186
187             if (!skipReply) reply(response);
188         }
189     }
190 }
191