PageRenderTime 24ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/src/org/gwt_websocketrpc/server/WsRpcServletWrapper.java

http://gwt-websocketrpc.googlecode.com/
Java | 271 lines | 179 code | 56 blank | 36 comment | 16 complexity | 1b9ff386f02172048c87a0f4dbc00d2c MD5 | raw file
  1. package org.gwt_websocketrpc.server;
  2. import static com.google.gwt.user.client.rpc.RpcRequestBuilder.MODULE_BASE_HEADER;
  3. import static com.google.gwt.user.client.rpc.RpcRequestBuilder.STRONG_NAME_HEADER;
  4. import static org.gwt_websocketrpc.shared.WsRpcConstants.WsRpcControlString;
  5. import static org.gwt_websocketrpc.server.ServerUtils.d;
  6. import java.io.IOException;
  7. import java.util.Map;
  8. import java.util.concurrent.ConcurrentHashMap;
  9. import javax.servlet.ServletConfig;
  10. import javax.servlet.ServletException;
  11. import javax.servlet.http.HttpServletRequest;
  12. import javax.servlet.http.HttpServletRequestWrapper;
  13. import javax.servlet.http.HttpServletResponse;
  14. import org.eclipse.jetty.websocket.WebSocket;
  15. import org.gwt_websocketrpc.server.websocket.WsOutboundStream;
  16. import com.google.gwt.rpc.client.impl.RemoteException;
  17. import com.google.gwt.rpc.server.ClientOracle;
  18. import com.google.gwt.rpc.server.RPC;
  19. import com.google.gwt.rpc.server.RpcServlet;
  20. import com.google.gwt.user.client.rpc.IncompatibleRemoteServiceException;
  21. import com.google.gwt.user.client.rpc.SerializationException;
  22. import com.google.gwt.user.server.rpc.RPCRequest;
  23. import com.google.gwt.user.server.rpc.UnexpectedException;
  24. @SuppressWarnings("serial")
  25. class WsRpcServletWrapper extends RpcServlet implements WebSocket {
  26. private static class RequestWrapper extends HttpServletRequestWrapper {
  27. private String permStrongName;
  28. private String reqModuleBasePath;
  29. private final String contextPath;
  30. public RequestWrapper(HttpServletRequest request) {
  31. super(request);
  32. contextPath = request.getContextPath();
  33. }
  34. void setPermStrongName(String permStrongName) {
  35. this.permStrongName = permStrongName;
  36. }
  37. void setReqModuleBasePath(String reqModuleBasePath) {
  38. this.reqModuleBasePath = reqModuleBasePath;
  39. }
  40. @Override
  41. public String getHeader(final String name) {
  42. return (name.equals(STRONG_NAME_HEADER)) ? permStrongName : (name
  43. .equals(MODULE_BASE_HEADER)) ? reqModuleBasePath : super
  44. .getHeader(name);
  45. }
  46. @Override
  47. public String getContextPath() {
  48. return contextPath;
  49. }
  50. }
  51. private final Map<Integer, PushCallbackImpl<?>> reqCallbackMap = new ConcurrentHashMap<Integer, PushCallbackImpl<?>>();
  52. private boolean[] wsInitialized = { false };
  53. private final WsRpcServletWrapper.RequestWrapper wrapReq;
  54. private final Class serviceClass;
  55. private final Object serviceInst;
  56. private Outbound o;
  57. private ClientOracle oracle;
  58. private final ThreadLocal<PushCallbackImpl<?>[]> tlrcb;
  59. public WsRpcServletWrapper(ServletConfig sc,
  60. ThreadLocal<PushCallbackImpl<?>[]> tlrcb, Object instance,
  61. HttpServletRequest req) {
  62. assert sc != null;
  63. assert instance != null;
  64. assert tlrcb != null;
  65. assert req != null;
  66. try {
  67. init(sc);
  68. } catch (ServletException e) {
  69. e.printStackTrace();
  70. }
  71. this.tlrcb = tlrcb;
  72. this.serviceInst = instance;
  73. this.serviceClass = instance.getClass();
  74. this.wrapReq = new RequestWrapper(req);
  75. validateThreadLocalData();
  76. }
  77. public void onConnect(Outbound arg0) {
  78. d("onConnect");
  79. o = arg0;
  80. }
  81. public void onDisconnect() {
  82. d("onDisconnect");
  83. o = null;
  84. }
  85. private void validateThreadLocalData() {
  86. if (perThreadRequest == null) {
  87. perThreadRequest = new ThreadLocal<HttpServletRequest>();
  88. }
  89. if (perThreadResponse == null) {
  90. perThreadResponse = new ThreadLocal<HttpServletResponse>();
  91. }
  92. }
  93. public void onMessage(byte arg0, byte[] arg1, int arg2, int arg3) {
  94. }
  95. public void onMessage(byte arg0, String arg1) {
  96. d("onMessage:");
  97. d(arg1);
  98. d("\n");
  99. // First message encodes Strong Name and Module Base Path.
  100. // (Needs to be synchronized in case multiple messages
  101. // are received at the sametime)
  102. synchronized (wsInitialized) {
  103. if (!wsInitialized[0]) {
  104. final int snId = arg1.indexOf('!');
  105. // Usually these pieces of information are captured
  106. // from the HTTPServletRequest Headers of XHRs:
  107. // 1)
  108. // com.google.gwt.user.client.rpc.RpcRequestBuilder.STRONG_NAME_HEADER
  109. // 2)
  110. // com.google.gwt.user.client.rpc.RpcRequestBuilder.MODULE_BASE_HEADER
  111. // Instead we'll just grab it from the WebSocket message.
  112. final String permStrongName = arg1.substring(0, snId);
  113. final String reqModuleBasePath = arg1.substring(snId + 1);
  114. wrapReq.setPermStrongName(permStrongName);
  115. wrapReq.setReqModuleBasePath(reqModuleBasePath);
  116. d("init message: [permStrongName='" + permStrongName
  117. + "', reqModuleBasePath'" + reqModuleBasePath + "']");
  118. try {
  119. // Unfortunate...
  120. perThreadRequest.set(wrapReq);
  121. oracle = getClientOracle();
  122. wsInitialized[0] = true;
  123. } catch (Throwable e) {
  124. // Give a subclass a chance to either handle the exception
  125. // or
  126. // rethrow it
  127. //
  128. doUnexpectedFailure(e);
  129. } finally {
  130. // null the thread-locals to avoid holding request/response
  131. //
  132. perThreadRequest.set(null);
  133. }
  134. return;
  135. }
  136. }
  137. // ... Subsequent messages
  138. try {
  139. synchronized (this) {
  140. perThreadRequest.set(wrapReq);
  141. }
  142. // Parse Request id
  143. int lastCtrlCharId = arg1.indexOf('!');
  144. final int rid = Integer.parseInt(arg1.substring(0, lastCtrlCharId), 16);
  145. // RPC request?
  146. if (arg1.charAt(lastCtrlCharId + 1) != '!') {
  147. processRequest(rid, arg1.substring(lastCtrlCharId + 1));
  148. // RPC cancel?
  149. } else {
  150. final PushCallbackImpl cb = reqCallbackMap.remove(rid);
  151. if (cb != null)
  152. cb.cancel();
  153. else
  154. d("Cancel received for unknown request id = "+rid);
  155. }
  156. } catch (Throwable e) {
  157. e.printStackTrace();
  158. // Give a subclass a chance to either handle the exception or
  159. // rethrow it
  160. //
  161. doUnexpectedFailure(e);
  162. } finally {
  163. // null the thread-locals to avoid holding request/response
  164. //
  165. perThreadRequest.set(null);
  166. }
  167. }
  168. protected void processRequest(final int rid, String msg)
  169. throws SerializationException, IOException {
  170. final ClassLoader oldcl = Thread.currentThread().getContextClassLoader();
  171. final WsOutboundStream os = new WsOutboundStream(o);
  172. try {
  173. // Set the TCCL...
  174. // Possibly a bug in Jetty, WebSocketServlet's appear
  175. // to have the incorrect TCCL set. The Java App
  176. // ClassLoader is set, as opposed to the WebApp's
  177. // ClassLoader (More investigation is necessary).
  178. Thread.currentThread().setContextClassLoader(
  179. serviceClass.getClassLoader());
  180. final RPCRequest rpcRequest = RPC
  181. .decodeRequest(msg, serviceClass, oracle);
  182. onAfterRequestDeserialized(rpcRequest);
  183. final PushCallbackImpl<?>[] cb = tlrcb.get();
  184. try {
  185. cb[0] = new PushCallbackImpl(rpcRequest.getMethod().getReturnType(),
  186. oracle, rpcRequest.getMethod(), o, rid);
  187. // Add new request -> handler callback entry
  188. reqCallbackMap.put(rid, cb[0]);
  189. // Prefix response with Request ID
  190. os.write((Integer.toHexString(rid) + WsRpcControlString).getBytes());
  191. // Send
  192. RPC.invokeAndStreamResponse(serviceInst, rpcRequest.getMethod(),
  193. rpcRequest.getParameters(), oracle, os);
  194. os.flush();
  195. } catch (UnexpectedException e) {
  196. // Allow Async Callback for handlers
  197. if (e.getCause().getClass() != ResponseSentLater.class)
  198. throw e;
  199. } finally {
  200. cb[0] = null;
  201. }
  202. } catch (RemoteException ex) {
  203. throw new SerializationException("An exception was sent from the client",
  204. ex.getCause());
  205. } catch (IncompatibleRemoteServiceException ex) {
  206. log(
  207. "An IncompatibleRemoteServiceException was thrown while processing this call.",
  208. ex);
  209. RPC.streamResponseForFailure(oracle, os, ex);
  210. } finally {
  211. // Reset the TCCL
  212. Thread.currentThread().setContextClassLoader(oldcl);
  213. }
  214. }
  215. }