1 16 package com.google.gwt.user.server.rpc; 17 18 import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException; 19 import com.google.gwt.user.client.rpc.SerializationException; 20 21 import java.io.ByteArrayOutputStream ; 22 import java.io.IOException ; 23 import java.io.InputStream ; 24 import java.util.zip.GZIPOutputStream ; 25 26 import javax.servlet.ServletContext ; 27 import javax.servlet.ServletException ; 28 import javax.servlet.http.HttpServlet ; 29 import javax.servlet.http.HttpServletRequest ; 30 import javax.servlet.http.HttpServletResponse ; 31 32 37 public class RemoteServiceServlet extends HttpServlet { 38 42 private static final String ACCEPT_ENCODING = "Accept-Encoding"; 43 private static final String CHARSET_UTF8 = "UTF-8"; 44 private static final String CONTENT_ENCODING = "Content-Encoding"; 45 private static final String CONTENT_ENCODING_GZIP = "gzip"; 46 private static final String CONTENT_TYPE_TEXT_PLAIN_UTF8 = "text/plain; charset=utf-8"; 47 private static final String GENERIC_FAILURE_MSG = "The call failed on the server; see server log for details"; 48 49 53 private static final int UNCOMPRESSED_BYTE_SIZE_LIMIT = 256; 54 55 60 private static boolean acceptsGzipEncoding(HttpServletRequest request) { 61 assert (request != null); 62 63 String acceptEncoding = request.getHeader(ACCEPT_ENCODING); 64 if (null == acceptEncoding) { 65 return false; 66 } 67 68 return (acceptEncoding.indexOf(CONTENT_ENCODING_GZIP) != -1); 69 } 70 71 81 private static int estimateByteSize(final String buffer) { 82 return (buffer.length() * 2); 83 } 84 85 88 private static String readPayloadAsUtf8(HttpServletRequest request) 89 throws IOException , ServletException { 90 int contentLength = request.getContentLength(); 91 if (contentLength == -1) { 92 throw new ServletException ("Content-Length must be specified"); 94 } 95 96 String contentType = request.getContentType(); 97 boolean contentTypeIsOkay = false; 98 if (contentType != null) { 100 if (contentType.startsWith("text/plain")) { 102 if (contentType.indexOf("charset=") == -1) { 105 contentTypeIsOkay = true; 106 } else if (contentType.indexOf("charset=utf-8") != -1) { 107 contentTypeIsOkay = true; 108 } 109 } 110 } 111 if (!contentTypeIsOkay) { 112 throw new ServletException ( 113 "Content-Type must be 'text/plain' with 'charset=utf-8' (or unspecified charset)"); 114 } 115 InputStream in = request.getInputStream(); 116 try { 117 byte[] payload = new byte[contentLength]; 118 int offset = 0; 119 int len = contentLength; 120 int byteCount; 121 while (offset < contentLength) { 122 byteCount = in.read(payload, offset, len); 123 if (byteCount == -1) { 124 throw new ServletException ("Client did not send " + contentLength 125 + " bytes as expected"); 126 } 127 offset += byteCount; 128 len -= byteCount; 129 } 130 return new String (payload, "UTF-8"); 131 } finally { 132 if (in != null) { 133 in.close(); 134 } 135 } 136 } 137 138 private final ThreadLocal perThreadRequest = new ThreadLocal (); 139 140 private final ThreadLocal perThreadResponse = new ThreadLocal (); 141 142 145 public RemoteServiceServlet() { 146 } 147 148 155 public final void doPost(HttpServletRequest request, 156 HttpServletResponse response) { 157 try { 158 perThreadRequest.set(request); 161 perThreadResponse.set(response); 162 163 String requestPayload = readPayloadAsUtf8(request); 166 167 onBeforeRequestDeserialized(requestPayload); 170 171 String responsePayload = processCall(requestPayload); 175 176 onAfterResponseSerialized(responsePayload); 179 180 writeResponse(request, response, responsePayload); 183 return; 184 } catch (Throwable e) { 185 doUnexpectedFailure(e); 188 } finally { 189 perThreadRequest.set(null); 192 perThreadResponse.set(null); 193 } 194 } 195 196 219 public String processCall(String payload) throws SerializationException { 220 try { 221 RPCRequest rpcRequest = RPC.decodeRequest(payload, this.getClass()); 222 return RPC.invokeAndEncodeResponse(this, rpcRequest.getMethod(), 223 rpcRequest.getParameters()); 224 } catch (IncompatibleRemoteServiceException ex) { 225 return RPC.encodeResponseForFailure(null, ex); 226 } 227 } 228 229 249 protected void doUnexpectedFailure(Throwable e) { 250 ServletContext servletContext = getServletContext(); 251 servletContext.log("Exception while dispatching incoming RPC call", e); 252 253 respondWithFailure(getThreadLocalResponse()); 256 } 257 258 263 protected final HttpServletRequest getThreadLocalRequest() { 264 return (HttpServletRequest ) perThreadRequest.get(); 265 } 266 267 272 protected final HttpServletResponse getThreadLocalResponse() { 273 return (HttpServletResponse ) perThreadResponse.get(); 274 } 275 276 281 protected void onAfterResponseSerialized(String serializedResponse) { 282 } 283 284 289 protected void onBeforeRequestDeserialized(String serializedRequest) { 290 } 291 292 308 protected boolean shouldCompressResponse(HttpServletRequest request, 309 HttpServletResponse response, String responsePayload) { 310 return estimateByteSize(responsePayload) > UNCOMPRESSED_BYTE_SIZE_LIMIT; 311 } 312 313 318 private void respondWithFailure(HttpServletResponse response) { 319 try { 320 response.setContentType("text/plain"); 321 response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 322 response.getWriter().write(GENERIC_FAILURE_MSG); 323 } catch (IOException e) { 324 getServletContext().log( 325 "respondWithFailure failed while sending the previous failure to the client", 326 e); 327 } 328 } 329 330 333 private void writeResponse(HttpServletRequest request, 334 HttpServletResponse response, String responsePayload) throws IOException { 335 336 byte[] reply = responsePayload.getBytes(CHARSET_UTF8); 337 String contentType = CONTENT_TYPE_TEXT_PLAIN_UTF8; 338 339 if (acceptsGzipEncoding(request) 340 && shouldCompressResponse(request, response, responsePayload)) { 341 ByteArrayOutputStream output = null; 344 GZIPOutputStream gzipOutputStream = null; 345 Throwable caught = null; 346 try { 347 output = new ByteArrayOutputStream (reply.length); 348 gzipOutputStream = new GZIPOutputStream (output); 349 gzipOutputStream.write(reply); 350 gzipOutputStream.finish(); 351 gzipOutputStream.flush(); 352 response.setHeader(CONTENT_ENCODING, CONTENT_ENCODING_GZIP); 353 reply = output.toByteArray(); 354 } catch (IOException e) { 355 caught = e; 356 } finally { 357 if (null != gzipOutputStream) { 358 gzipOutputStream.close(); 359 } 360 if (null != output) { 361 output.close(); 362 } 363 } 364 365 if (caught != null) { 366 getServletContext().log("Unable to compress response", caught); 367 response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 368 return; 369 } 370 } 371 372 response.setContentLength(reply.length); 375 response.setContentType(contentType); 376 response.setStatus(HttpServletResponse.SC_OK); 377 response.getOutputStream().write(reply); 378 } 379 } 380 | Popular Tags |