Reworked to use servlet 3.1 async read support.
Showing
1 changed file
with
202 additions
and
49 deletions
1 | package com.brainfood.ofbiz; | 1 | package com.brainfood.ofbiz; |
2 | 2 | ||
3 | import java.io.ByteArrayOutputStream; | ||
3 | import java.io.IOException; | 4 | import java.io.IOException; |
4 | import java.io.PrintWriter; | 5 | import java.io.PrintWriter; |
6 | import java.io.BufferedOutputStream; | ||
5 | import java.io.BufferedReader; | 7 | import java.io.BufferedReader; |
6 | import java.io.StringReader; | 8 | import java.io.StringReader; |
7 | import java.io.InputStreamReader; | 9 | import java.io.InputStreamReader; |
8 | import java.io.InputStream; | 10 | import java.io.InputStream; |
9 | import java.io.File; | 11 | import java.io.File; |
12 | import java.io.FileInputStream; | ||
13 | import java.io.FileOutputStream; | ||
14 | import java.io.FileReader; | ||
15 | import java.io.OutputStream; | ||
10 | import java.util.Collections; | 16 | import java.util.Collections; |
11 | import java.util.Collection; | 17 | import java.util.Collection; |
12 | import java.util.Map; | 18 | import java.util.Map; |
13 | import java.net.URL; | 19 | import java.net.URL; |
14 | import java.net.MalformedURLException; | 20 | import java.net.MalformedURLException; |
21 | import java.security.DigestOutputStream; | ||
22 | import java.security.MessageDigest; | ||
23 | import java.security.NoSuchAlgorithmException; | ||
15 | import java.util.ArrayList; | 24 | import java.util.ArrayList; |
16 | import java.util.Set; | 25 | import java.util.Set; |
17 | import java.util.Date; | 26 | import java.util.Date; |
... | @@ -26,10 +35,17 @@ import java.util.Iterator; | ... | @@ -26,10 +35,17 @@ import java.util.Iterator; |
26 | import java.sql.Timestamp; | 35 | import java.sql.Timestamp; |
27 | 36 | ||
28 | import javax.script.ScriptContext; | 37 | import javax.script.ScriptContext; |
38 | import javax.servlet.AsyncContext; | ||
39 | import javax.servlet.AsyncEvent; | ||
40 | import javax.servlet.AsyncListener; | ||
41 | import javax.servlet.ReadListener; | ||
29 | import javax.servlet.RequestDispatcher; | 42 | import javax.servlet.RequestDispatcher; |
30 | import javax.servlet.ServletConfig; | 43 | import javax.servlet.ServletConfig; |
31 | import javax.servlet.ServletContext; | 44 | import javax.servlet.ServletContext; |
32 | import javax.servlet.ServletException; | 45 | import javax.servlet.ServletException; |
46 | import javax.servlet.ServletInputStream; | ||
47 | import javax.servlet.ServletOutputStream; | ||
48 | import javax.servlet.WriteListener; | ||
33 | import javax.servlet.http.HttpServlet; | 49 | import javax.servlet.http.HttpServlet; |
34 | import javax.servlet.http.HttpServletRequest; | 50 | import javax.servlet.http.HttpServletRequest; |
35 | import javax.servlet.http.HttpServletResponse; | 51 | import javax.servlet.http.HttpServletResponse; |
... | @@ -38,6 +54,7 @@ import javax.servlet.http.Cookie; | ... | @@ -38,6 +54,7 @@ import javax.servlet.http.Cookie; |
38 | import javax.servlet.http.Part; | 54 | import javax.servlet.http.Part; |
39 | 55 | ||
40 | import org.ofbiz.base.lang.JSON; | 56 | import org.ofbiz.base.lang.JSON; |
57 | import org.ofbiz.base.util.Base64; | ||
41 | import org.ofbiz.base.util.Debug; | 58 | import org.ofbiz.base.util.Debug; |
42 | import org.ofbiz.base.util.GroovyUtil; | 59 | import org.ofbiz.base.util.GroovyUtil; |
43 | import org.ofbiz.base.util.ScriptHelper; | 60 | import org.ofbiz.base.util.ScriptHelper; |
... | @@ -62,11 +79,11 @@ import org.ofbiz.service.ServiceContainer; | ... | @@ -62,11 +79,11 @@ import org.ofbiz.service.ServiceContainer; |
62 | 79 | ||
63 | import org.apache.commons.csv.CSVFormat; | 80 | import org.apache.commons.csv.CSVFormat; |
64 | import org.apache.commons.csv.CSVRecord; | 81 | import org.apache.commons.csv.CSVRecord; |
65 | 82 | import org.apache.commons.fileupload.MultipartStream; | |
66 | import org.apache.commons.fileupload.*; | 83 | import org.apache.commons.fileupload.ParameterParser; |
67 | import org.apache.commons.fileupload.disk.DiskFileItemFactory; | 84 | import org.apache.http.HeaderElement; |
68 | import org.apache.commons.fileupload.disk.DiskFileItem; | 85 | import org.apache.http.NameValuePair; |
69 | import org.apache.commons.fileupload.servlet.ServletFileUpload; | 86 | import org.apache.http.message.BasicHeaderValueParser; |
70 | 87 | ||
71 | import groovy.lang.GroovyClassLoader; | 88 | import groovy.lang.GroovyClassLoader; |
72 | import groovy.lang.Script; | 89 | import groovy.lang.Script; |
... | @@ -157,9 +174,98 @@ public class DirectControlServlet extends HttpServlet { | ... | @@ -157,9 +174,98 @@ public class DirectControlServlet extends HttpServlet { |
157 | return; | 174 | return; |
158 | } | 175 | } |
159 | } | 176 | } |
160 | Debug.logInfo("Service name " + serviceName, module); | 177 | Debug.logInfo("Service name %s" +serviceName + " mapped for " + pathInfo + "#" + method, module, serviceName, pathInfo); |
178 | |||
179 | AsyncContext context = request.startAsync(); | ||
180 | AsyncHandler asyncHandler = new AsyncHandler(serviceName, request, response, context); | ||
181 | context.addListener(asyncHandler); | ||
182 | asyncHandler.sin.setReadListener(asyncHandler); | ||
183 | } catch (Throwable t) { | ||
184 | response.setStatus(500); | ||
185 | PrintWriter writer = response.getWriter(); | ||
186 | Debug.logInfo("Exception processing request", module); | ||
187 | Debug.logInfo(t, module); | ||
188 | while (t != null) { | ||
189 | t.printStackTrace(writer); | ||
190 | t = t.getCause(); | ||
191 | } | ||
192 | writer.flush(); | ||
193 | writer.close(); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | protected interface Cleanup { | ||
198 | void cleanup(); | ||
199 | } | ||
200 | |||
201 | protected class AsyncHandler implements ReadListener, WriteListener, AsyncListener { | ||
202 | protected final String serviceName; | ||
203 | protected final String outputHandler; | ||
204 | |||
205 | protected final File directory; | ||
206 | protected final HttpServletRequest request; | ||
207 | protected final HttpServletResponse response; | ||
208 | |||
209 | protected final ServletInputStream sin; | ||
210 | protected final AsyncContext context; | ||
211 | |||
212 | protected final File tmpFile; | ||
213 | protected final OutputStream tmpOut; | ||
214 | |||
215 | protected final List<Cleanup> cleanups = new ArrayList<Cleanup>(); | ||
216 | |||
217 | protected AsyncHandler(String serviceName, HttpServletRequest request, HttpServletResponse response, AsyncContext context) throws IOException, NoSuchAlgorithmException { | ||
218 | this.directory = (File) request.getServletContext().getAttribute("javax.servlet.context.tempdir"); | ||
219 | this.request = request; | ||
220 | this.response = response; | ||
221 | |||
222 | this.sin = request.getInputStream(); | ||
223 | this.context = context; | ||
224 | |||
225 | this.tmpFile = deleteFileWhenDone(File.createTempFile("dcs", ".body", directory)); | ||
226 | this.tmpOut = new BufferedOutputStream(new FileOutputStream(tmpFile)); | ||
227 | |||
228 | String outputHandler = "JSON"; | ||
229 | if (serviceName.indexOf("|") != -1) { | ||
230 | String[] parts = serviceName.split("\\|"); | ||
231 | serviceName = parts[0]; | ||
232 | outputHandler = parts[1]; | ||
233 | } | ||
234 | this.serviceName = serviceName; | ||
235 | this.outputHandler = outputHandler; | ||
236 | } | ||
237 | |||
238 | |||
239 | public void onAllDataRead() throws IOException { | ||
240 | //System.err.println("onAllDataRead"); | ||
241 | sin.close(); | ||
242 | |||
243 | tmpOut.flush(); | ||
244 | tmpOut.close(); | ||
245 | |||
246 | try { | ||
247 | processRequest(); | ||
248 | } catch (IOException e) { | ||
249 | throw e; | ||
250 | } catch (Exception e) { | ||
251 | e.printStackTrace(); | ||
252 | throw (IOException) new IOException(e.getMessage()).initCause(e); | ||
253 | } finally { | ||
254 | context.complete(); | ||
255 | } | ||
256 | } | ||
161 | 257 | ||
162 | // Load context | 258 | protected File deleteFileWhenDone(final File file) { |
259 | file.deleteOnExit(); | ||
260 | cleanups.add(new Cleanup() { | ||
261 | public void cleanup() { | ||
262 | file.delete(); | ||
263 | } | ||
264 | }); | ||
265 | return file; | ||
266 | } | ||
267 | |||
268 | protected void processRequest() throws Exception { | ||
163 | Map<String, Object> context = new HashMap<String, Object>(); | 269 | Map<String, Object> context = new HashMap<String, Object>(); |
164 | 270 | ||
165 | // Directly copy request parameters into context. | 271 | // Directly copy request parameters into context. |
... | @@ -172,21 +278,23 @@ public class DirectControlServlet extends HttpServlet { | ... | @@ -172,21 +278,23 @@ public class DirectControlServlet extends HttpServlet { |
172 | 278 | ||
173 | // Determine type of request and load the context from JSON content or | 279 | // Determine type of request and load the context from JSON content or |
174 | // parameter values accordingly | 280 | // parameter values accordingly |
175 | if (contentType != null) { | 281 | String rawContentType = request.getContentType(); |
176 | int semi = contentType.indexOf(";"); | 282 | String contentType = rawContentType; |
283 | if (rawContentType != null) { | ||
284 | int semi = rawContentType.indexOf(";"); | ||
177 | if (semi != -1) { | 285 | if (semi != -1) { |
178 | contentType = contentType.substring(0, semi); | 286 | contentType = rawContentType.substring(0, semi); |
179 | } | 287 | } |
180 | } | 288 | } |
181 | 289 | ||
182 | if ("application/json".equals(contentType)) { | 290 | if ("application/json".equals(contentType)) { |
183 | // Read request body as JSON and insert into the context | 291 | // Read request body as JSON and insert into the context |
184 | Map<String, Object> items = UtilGenerics.cast(JSON.from(UtilIO.readString(request.getReader())).toObject(Map.class)); | 292 | Map<String, Object> items = UtilGenerics.cast(JSON.from(UtilIO.readString(new FileReader(tmpFile))).toObject(Map.class)); |
185 | for (Object key : items.keySet()) { | 293 | for (Object key : items.keySet()) { |
186 | context.put((String) key, items.get(key)); | 294 | context.put((String) key, items.get(key)); |
187 | } | 295 | } |
188 | } else if ("text/csv".equals(contentType)) { | 296 | } else if ("text/csv".equals(contentType)) { |
189 | Iterable<CSVRecord> records = CSVFormat.EXCEL.parse(request.getReader()); | 297 | Iterable<CSVRecord> records = CSVFormat.EXCEL.parse(new FileReader(tmpFile)); |
190 | List<List<String>> data = new ArrayList<List<String>>(); | 298 | List<List<String>> data = new ArrayList<List<String>>(); |
191 | for (CSVRecord record : records) { | 299 | for (CSVRecord record : records) { |
192 | List<String> row = new ArrayList<String>(); | 300 | List<String> row = new ArrayList<String>(); |
... | @@ -198,30 +306,55 @@ public class DirectControlServlet extends HttpServlet { | ... | @@ -198,30 +306,55 @@ public class DirectControlServlet extends HttpServlet { |
198 | data.add(row); | 306 | data.add(row); |
199 | } | 307 | } |
200 | context.put("data", data); | 308 | context.put("data", data); |
201 | } else if (contentType != null && contentType.indexOf("multipart/form-data") != -1) { | 309 | } else if (rawContentType != null && rawContentType.indexOf("multipart/form-data") != -1) { |
202 | // Create a factory for disk-based file items | 310 | //System.err.println("contentType[" + rawContentType + "]"); |
203 | DiskFileItemFactory factory = new DiskFileItemFactory(); | 311 | String boundary = rawContentType.substring("multipart/form-data; boundary=".length()); |
204 | 312 | MessageDigest digest = MessageDigest.getInstance("SHA-256"); | |
205 | // Configure a repository (to ensure a secure temp location is used) | 313 | MultipartStream multipartStream = new MultipartStream(new FileInputStream(tmpFile), boundary.getBytes(), 65536, null); |
206 | ServletContext servletContext = config.getServletContext(); | 314 | ParameterParser parameterParser = new ParameterParser(); |
207 | File repository = (File) servletContext | 315 | |
208 | .getAttribute("javax.servlet.context.tempdir"); | 316 | boolean hasNext = multipartStream.skipPreamble(); |
209 | factory.setRepository(repository); | 317 | while (hasNext) { |
210 | 318 | String[] headerLines = multipartStream.readHeaders().split("[\r\n]+(?! )"); | |
211 | ServletFileUpload upload = new ServletFileUpload(factory); | 319 | Map<String, String> headerData = new HashMap<String, String>(); |
212 | 320 | for (String headerLine: headerLines) { | |
213 | //Map<String, FileItem> itemMap = new HashMap<String, FileItem>(); | 321 | //System.err.println("headerLine[" + headerLine + "]"); |
214 | 322 | String[] headerNameAndRawValue = headerLine.split(": ", 2); | |
215 | List<FileItem> items = (List<FileItem>)upload.parseRequest(request); | 323 | headerData.put(headerNameAndRawValue[0].toLowerCase(), headerNameAndRawValue[1]); |
216 | for (FileItem item : items) { | 324 | HeaderElement[] headerElements = BasicHeaderValueParser.INSTANCE.parseElements(headerNameAndRawValue[1], null); |
217 | Debug.logInfo("PART: " + item.getFieldName(), module); | 325 | for (HeaderElement headerElement: headerElements) { |
218 | context.put(item.getFieldName(), item.getString()); | 326 | String headerName = headerElement.getName(); |
327 | String prefix = (headerNameAndRawValue[0] + "." + headerName).toLowerCase(); | ||
328 | headerData.put(prefix, headerElement.getValue()); | ||
329 | for (NameValuePair nameValuePair: headerElement.getParameters()) { | ||
330 | headerData.put(prefix + "." + nameValuePair.getName(), nameValuePair.getValue()); | ||
331 | } | ||
332 | } | ||
333 | } | ||
334 | //System.err.println("header=" + JSON.from(headerData).toString()); | ||
335 | String fieldName = headerData.get("content-disposition.form-data.name"); | ||
336 | String fileName = headerData.get("content-disposition.form-data.filename"); | ||
337 | if (fileName == null) { | ||
338 | ByteArrayOutputStream baos = new ByteArrayOutputStream(); | ||
339 | multipartStream.readBodyData(baos); | ||
340 | context.put(fieldName, baos.toString()); | ||
341 | } else { | ||
342 | File tmpFilePart = deleteFileWhenDone(File.createTempFile("dcs", ".part", directory)); | ||
343 | digest.reset(); | ||
344 | DigestOutputStream digestOut = new DigestOutputStream(new FileOutputStream(tmpFilePart), digest); | ||
345 | multipartStream.readBodyData(digestOut); | ||
346 | context.put(fieldName, tmpFilePart); | ||
347 | String sha256 = new String(Base64.base64Encode(digest.digest())); | ||
348 | context.put(fieldName + ".contentType", headerData.get("content-type")); | ||
349 | context.put(fieldName + ".sha256", sha256); | ||
350 | } | ||
351 | hasNext = multipartStream.readBoundary(); | ||
219 | } | 352 | } |
220 | } else { | 353 | } else { |
221 | // Check if the request is a backbone style "emulateJSON" request | 354 | // Check if the request is a backbone style "emulateJSON" request |
222 | if (contentType != null && contentType.indexOf("x-www-form-urlencoded") != -1 && request.getParameter("model") != null) { | 355 | if (contentType != null && contentType.indexOf("x-www-form-urlencoded") != -1 && request.getParameter("model") != null) { |
223 | Debug.logInfo("MODEL: " + request.getParameter("model"), module); | 356 | Debug.logInfo("MODEL: " + request.getParameter("model"), module); |
224 | Map<String, Object> items = UtilGenerics.cast(JSON.from(UtilIO.readString(request.getReader())).toObject(Map.class)); | 357 | Map<String, Object> items = UtilGenerics.cast(JSON.from(UtilIO.readString(new FileReader(tmpFile))).toObject(Map.class)); |
225 | for (Object key : items.keySet()) { | 358 | for (Object key : items.keySet()) { |
226 | if (!"sessionId".equals(key)) { | 359 | if (!"sessionId".equals(key)) { |
227 | context.put((String) key, items.get(key)); | 360 | context.put((String) key, items.get(key)); |
... | @@ -234,14 +367,6 @@ public class DirectControlServlet extends HttpServlet { | ... | @@ -234,14 +367,6 @@ public class DirectControlServlet extends HttpServlet { |
234 | LocalDispatcher dispatcher = getDispatcher(request.getServletContext()); | 367 | LocalDispatcher dispatcher = getDispatcher(request.getServletContext()); |
235 | 368 | ||
236 | // Check if there is an output handler | 369 | // Check if there is an output handler |
237 | String outputHandler = "JSON"; | ||
238 | if (serviceName.indexOf("|") != -1) { | ||
239 | String[] parts = serviceName.split("\\|"); | ||
240 | serviceName = parts[0]; | ||
241 | outputHandler = parts[1]; | ||
242 | } | ||
243 | |||
244 | Debug.logInfo("Service name" +serviceName + " mapped for " + pathInfo + "#" + method, module); | ||
245 | 370 | ||
246 | // If the sessionId parameter is set, attempt to look up the corresponding | 371 | // If the sessionId parameter is set, attempt to look up the corresponding |
247 | // UserLogin and apply it to the service context | 372 | // UserLogin and apply it to the service context |
... | @@ -373,17 +498,45 @@ public class DirectControlServlet extends HttpServlet { | ... | @@ -373,17 +498,45 @@ public class DirectControlServlet extends HttpServlet { |
373 | if ("PDF".equals(outputHandler)) { | 498 | if ("PDF".equals(outputHandler)) { |
374 | LibreOfficeRenderer.service(request, response, result); | 499 | LibreOfficeRenderer.service(request, response, result); |
375 | } | 500 | } |
376 | } catch (Throwable t) { | 501 | |
377 | response.setStatus(500); | 502 | //sout.setWriteListener(this); |
378 | PrintWriter writer = response.getWriter(); | ||
379 | Debug.logInfo("Exception processing request", module); | ||
380 | Debug.logInfo(t, module); | ||
381 | while (t != null) { | ||
382 | t.printStackTrace(writer); | ||
383 | t = t.getCause(); | ||
384 | } | 503 | } |
385 | writer.flush(); | 504 | |
386 | writer.close(); | 505 | |
506 | public void onDataAvailable() throws IOException { | ||
507 | byte[] buffer = new byte[65536]; | ||
508 | int numRead; | ||
509 | while (sin.isReady() && (numRead = sin.read(buffer)) > 0) { | ||
510 | tmpOut.write(buffer, 0, numRead); | ||
511 | } | ||
512 | } | ||
513 | |||
514 | public void onWritePossible() throws IOException { | ||
515 | context.complete(); | ||
516 | } | ||
517 | |||
518 | public void onError(Throwable t) { | ||
519 | t.printStackTrace(); | ||
520 | context.complete(); | ||
521 | } | ||
522 | |||
523 | public void onComplete(AsyncEvent event) throws IOException { | ||
524 | //System.err.println("onComplete"); | ||
525 | for (Cleanup cleanup: cleanups) { | ||
526 | cleanup.cleanup(); | ||
527 | } | ||
528 | } | ||
529 | |||
530 | public void onError(AsyncEvent event) { | ||
531 | event.getThrowable().printStackTrace(); | ||
532 | } | ||
533 | |||
534 | public void onStartAsync(AsyncEvent event) { | ||
535 | //System.err.println("onStartAsync"); | ||
536 | } | ||
537 | |||
538 | public void onTimeout(AsyncEvent event) { | ||
539 | ////System.err.println("onTimeout"); | ||
387 | } | 540 | } |
388 | } | 541 | } |
389 | 542 | ... | ... |
-
Please register or sign in to post a comment