PageRenderTime 3924ms CodeModel.GetById 33ms RepoModel.GetById 15ms app.codeStats 1ms

/plugins/bayeux/src/main/java/org/atmosphere/plugin/bayeux/AtmosphereBayeuxServlet.java

https://github.com/ayush/atmosphere
Java | 295 lines | 221 code | 37 blank | 37 comment | 84 complexity | bf97aaeba93355915b12846948e1e21c MD5 | raw file
  1. //========================================================================
  2. //Copyright 2007 Mort Bay Consulting Pty. Ltd.
  3. //------------------------------------------------------------------------
  4. //Licensed under the Apache License, Version 2.0 (the "License");
  5. //you may not use this file except in compliance with the License.
  6. //You may obtain a copy of the License at
  7. //http://www.apache.org/licenses/LICENSE-2.0
  8. //Unless required by applicable law or agreed to in writing, software
  9. //distributed under the License is distributed on an "AS IS" BASIS,
  10. //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. //See the License for the specific language governing permissions and
  12. //limitations under the License.
  13. //========================================================================
  14. package org.atmosphere.plugin.bayeux;
  15. import org.atmosphere.cpr.AtmosphereResource;
  16. import org.atmosphere.cpr.AtmosphereServlet;
  17. import org.cometd.Bayeux;
  18. import org.cometd.Client;
  19. import org.cometd.Message;
  20. import org.cometd.server.AbstractBayeux;
  21. import org.cometd.server.AbstractCometdServlet;
  22. import org.cometd.server.ClientImpl;
  23. import org.cometd.server.JSONTransport;
  24. import org.cometd.server.MessageImpl;
  25. import org.cometd.server.Transport;
  26. import org.eclipse.jetty.util.ArrayQueue;
  27. import org.eclipse.jetty.util.StringUtil;
  28. import org.eclipse.jetty.util.log.Log;
  29. import javax.servlet.ServletException;
  30. import javax.servlet.http.HttpServletRequest;
  31. import javax.servlet.http.HttpServletResponse;
  32. import java.io.IOException;
  33. import java.nio.ByteBuffer;
  34. import java.util.ArrayList;
  35. import java.util.List;
  36. public class AtmosphereBayeuxServlet extends AbstractCometdServlet {
  37. public final static int __DEFAULT_REFS_THRESHOLD = 0;
  38. protected int _refsThreshold = __DEFAULT_REFS_THRESHOLD;
  39. String _responseBuffer;
  40. @Override
  41. public void init() throws ServletException {
  42. String refsThreshold = getInitParameter("refsThreshold");
  43. if (refsThreshold != null)
  44. _refsThreshold = Integer.parseInt(refsThreshold);
  45. if (_refsThreshold > 0) {
  46. String server = getServletContext().getServerInfo();
  47. if (server.startsWith("jetty/6"))
  48. _responseBuffer = "org.mortbay.jetty.ResponseBuffer";
  49. else if (server.startsWith("jetty/"))
  50. _responseBuffer = "org.eclipse.jetty.server.ResponseBuffer";
  51. else
  52. _refsThreshold = 0;
  53. }
  54. super.init();
  55. }
  56. /* ------------------------------------------------------------ */
  57. @Override
  58. protected AbstractBayeux newBayeux() {
  59. return new AtmosphereContinuationBayeux();
  60. }
  61. /* ------------------------------------------------------------ */
  62. @Override
  63. protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
  64. // Look for an existing client and protect from context restarts
  65. Object clientObj = request.getAttribute(CLIENT_ATTR);
  66. Transport transport = null;
  67. int received = -1;
  68. boolean metaConnectDeliveryOnly = false;
  69. boolean pendingResponse = false;
  70. boolean metaConnect = false;
  71. final boolean initial;
  72. // Have we seen this request before
  73. AtmosphereBayeuxClient client = (clientObj instanceof ClientImpl) ? (AtmosphereBayeuxClient) clientObj : null;
  74. if (client != null) {
  75. initial = false;
  76. // yes - extract saved properties
  77. transport = (Transport) request.getAttribute(TRANSPORT_ATTR);
  78. transport.setResponse(response);
  79. metaConnectDeliveryOnly = client.isMetaConnectDeliveryOnly() || transport.isMetaConnectDeliveryOnly();
  80. metaConnect = true;
  81. } else {
  82. initial = true;
  83. Message[] messages = getMessages(request);
  84. received = messages.length;
  85. /* check jsonp parameter */
  86. String jsonpParam = request.getParameter("jsonp");
  87. // Handle all received messages
  88. try {
  89. for (Message message : messages) {
  90. if (jsonpParam != null)
  91. message.put("jsonp", jsonpParam);
  92. if (client == null) {
  93. String clientId = (String) message.get(AbstractBayeux.CLIENT_FIELD);
  94. client = (AtmosphereBayeuxClient) _bayeux.getClient(clientId);
  95. // If no client, SHOULD be a handshake, so force a
  96. // transport and handle
  97. if (client == null) {
  98. // Setup a browser ID
  99. String browser_id = findBrowserId(request);
  100. if (browser_id == null)
  101. browser_id = setBrowserId(request, response);
  102. if (transport == null) {
  103. transport = _bayeux.newTransport(client, message);
  104. transport.setResponse(response);
  105. metaConnectDeliveryOnly = transport.isMetaConnectDeliveryOnly();
  106. }
  107. _bayeux.handle(null, transport, message);
  108. message = null;
  109. continue;
  110. }
  111. }
  112. String browser_id = findBrowserId(request);
  113. if (browser_id != null && (client.getBrowserId() == null || !client.getBrowserId().equals(browser_id)))
  114. client.setBrowserId(browser_id);
  115. // resolve transport
  116. if (transport == null) {
  117. transport = _bayeux.newTransport(client, message);
  118. transport.setResponse(response);
  119. metaConnectDeliveryOnly = client.isMetaConnectDeliveryOnly() || transport.isMetaConnectDeliveryOnly();
  120. }
  121. // Tell client to hold messages as a response is likely to
  122. // be sent.
  123. if (!metaConnectDeliveryOnly && !pendingResponse) {
  124. pendingResponse = true;
  125. client.responsePending();
  126. }
  127. if (Bayeux.META_CONNECT.equals(message.getChannel()))
  128. metaConnect = true;
  129. _bayeux.handle(client, transport, message);
  130. }
  131. }
  132. finally {
  133. for (Message message : messages)
  134. ((MessageImpl) message).decRef();
  135. if (pendingResponse) {
  136. client.responded();
  137. }
  138. }
  139. }
  140. Message metaConnectReply = null;
  141. // Do we need to wait for messages
  142. if (transport != null) {
  143. metaConnectReply = transport.getMetaConnectReply();
  144. if (metaConnectReply != null) {
  145. long timeout = client.getTimeout();
  146. if (timeout == 0)
  147. timeout = _bayeux.getTimeout();
  148. //Continuation continuation=ContinuationSupport.getContinuation(request);
  149. AtmosphereResource<HttpServletRequest, HttpServletResponse> continuation
  150. = (AtmosphereResource) request.getAttribute(
  151. AtmosphereServlet.ATMOSPHERE_RESOURCE);
  152. // Get messages or wait
  153. synchronized (client) {
  154. if (!client.hasNonLazyMessages() && initial && received <= 1) {
  155. // save state and suspend
  156. request.setAttribute(CLIENT_ATTR, client);
  157. request.setAttribute(TRANSPORT_ATTR, transport);
  158. continuation.suspend(timeout, false);
  159. client.setContinuation(continuation);
  160. return;
  161. }
  162. }
  163. client.setContinuation(null);
  164. transport.setMetaConnectReply(null);
  165. } else if (client != null) {
  166. client.access();
  167. }
  168. }
  169. if (client != null) {
  170. if (metaConnectDeliveryOnly && !metaConnect) {
  171. // wake up any long poll
  172. client.resume();
  173. } else {
  174. List<Message> messages;
  175. synchronized (client) {
  176. client.doDeliverListeners();
  177. ArrayQueue<Message> clientMessages = (ArrayQueue<Message>) client.getQueue();
  178. // Copy the messages to avoid synchronization
  179. messages = new ArrayList<Message>(clientMessages);
  180. // Empty client's queue
  181. clientMessages.clear();
  182. }
  183. final int size = messages.size();
  184. for (int i = 0; i < size; i++) {
  185. final Message message = messages.get(i);
  186. final MessageImpl mesgImpl = (message instanceof MessageImpl) ? (MessageImpl) message : null;
  187. // Can we short cut the message?
  188. if (i == 0 && size == 1 && mesgImpl != null && _refsThreshold > 0 && metaConnectReply != null && transport instanceof JSONTransport) {
  189. // is there a response already prepared
  190. ByteBuffer buffer = mesgImpl.getBuffer();
  191. if (buffer != null) {
  192. // Send pre-prepared buffer
  193. request.setAttribute("org.mortbay.jetty.ResponseBuffer", buffer);
  194. if (metaConnectReply instanceof MessageImpl)
  195. ((MessageImpl) metaConnectReply).decRef();
  196. metaConnectReply = null;
  197. transport = null;
  198. mesgImpl.decRef();
  199. continue;
  200. } else if (mesgImpl.getRefs() >= _refsThreshold) {
  201. // create multi-use buffer
  202. byte[] contentBytes = ("[" + mesgImpl.getJSON() + ",{\"" + Bayeux.SUCCESSFUL_FIELD + "\":true,\"" + Bayeux.CHANNEL_FIELD
  203. + "\":\"" + Bayeux.META_CONNECT + "\"}]").getBytes(StringUtil.__UTF8);
  204. int contentLength = contentBytes.length;
  205. String headerString = "HTTP/1.1 200 OK\r\n" + "Content-Type: text/json; charset=utf-8\r\n" + "Content-Length: "
  206. + contentLength + "\r\n" + "\r\n";
  207. byte[] headerBytes = headerString.getBytes(StringUtil.__UTF8);
  208. buffer = ByteBuffer.allocateDirect(headerBytes.length + contentLength);
  209. buffer.put(headerBytes);
  210. buffer.put(contentBytes);
  211. buffer.flip();
  212. mesgImpl.setBuffer(buffer);
  213. request.setAttribute("org.mortbay.jetty.ResponseBuffer", buffer);
  214. metaConnectReply = null;
  215. if (metaConnectReply instanceof MessageImpl)
  216. ((MessageImpl) metaConnectReply).decRef();
  217. transport = null;
  218. mesgImpl.decRef();
  219. continue;
  220. }
  221. }
  222. if (message != null)
  223. transport.send(message);
  224. if (mesgImpl != null)
  225. mesgImpl.decRef();
  226. }
  227. if (metaConnectReply != null) {
  228. metaConnectReply = _bayeux.extendSendMeta(client, metaConnectReply);
  229. transport.send(metaConnectReply);
  230. if (metaConnectReply instanceof MessageImpl)
  231. ((MessageImpl) metaConnectReply).decRef();
  232. }
  233. }
  234. }
  235. if (transport != null)
  236. transport.complete();
  237. }
  238. public void destroy() {
  239. AtmosphereContinuationBayeux bayeux = (AtmosphereContinuationBayeux) _bayeux;
  240. if (bayeux != null) {
  241. for (Client c : bayeux.getClients()) {
  242. if (c instanceof AtmosphereBayeuxClient) {
  243. AtmosphereBayeuxClient client = (AtmosphereBayeuxClient) c;
  244. AtmosphereResource continuation = client.getContinuation();
  245. client.setContinuation(null);
  246. if (continuation != null && continuation.getAtmosphereResourceEvent().isSuspended()) {
  247. try {
  248. ((HttpServletResponse) continuation.getResponse()).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
  249. } catch (IOException e) {
  250. Log.ignore(e);
  251. }
  252. }
  253. }
  254. }
  255. bayeux.destroy();
  256. }
  257. }
  258. }