Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

PushRequestHandler.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.server.communication;
  17. import java.io.IOException;
  18. import java.util.logging.Level;
  19. import java.util.logging.Logger;
  20. import javax.servlet.ServletConfig;
  21. import javax.servlet.ServletException;
  22. import org.atmosphere.cache.UUIDBroadcasterCache;
  23. import org.atmosphere.client.TrackMessageSizeInterceptor;
  24. import org.atmosphere.cpr.ApplicationConfig;
  25. import org.atmosphere.cpr.AtmosphereFramework;
  26. import org.atmosphere.cpr.AtmosphereFramework.AtmosphereHandlerWrapper;
  27. import org.atmosphere.cpr.AtmosphereHandler;
  28. import org.atmosphere.cpr.AtmosphereInterceptor;
  29. import org.atmosphere.cpr.AtmosphereRequest;
  30. import org.atmosphere.cpr.AtmosphereResponse;
  31. import org.atmosphere.interceptor.HeartbeatInterceptor;
  32. import org.atmosphere.util.VoidAnnotationProcessor;
  33. import com.vaadin.server.RequestHandler;
  34. import com.vaadin.server.ServiceDestroyEvent;
  35. import com.vaadin.server.ServiceDestroyListener;
  36. import com.vaadin.server.ServiceException;
  37. import com.vaadin.server.ServletPortletHelper;
  38. import com.vaadin.server.SessionExpiredHandler;
  39. import com.vaadin.server.VaadinRequest;
  40. import com.vaadin.server.VaadinResponse;
  41. import com.vaadin.server.VaadinServletRequest;
  42. import com.vaadin.server.VaadinServletResponse;
  43. import com.vaadin.server.VaadinServletService;
  44. import com.vaadin.server.VaadinSession;
  45. import com.vaadin.shared.communication.PushConstants;
  46. /**
  47. * Handles requests to open a push (bidirectional) communication channel between
  48. * the client and the server. After the initial request, communication through
  49. * the push channel is managed by {@link PushAtmosphereHandler} and
  50. * {@link PushHandler}
  51. *
  52. * @author Vaadin Ltd
  53. * @since 7.1
  54. */
  55. public class PushRequestHandler implements RequestHandler,
  56. SessionExpiredHandler {
  57. private AtmosphereFramework atmosphere;
  58. private PushHandler pushHandler;
  59. public PushRequestHandler(VaadinServletService service)
  60. throws ServiceException {
  61. service.addServiceDestroyListener(new ServiceDestroyListener() {
  62. @Override
  63. public void serviceDestroy(ServiceDestroyEvent event) {
  64. destroy();
  65. }
  66. });
  67. final ServletConfig vaadinServletConfig = service.getServlet()
  68. .getServletConfig();
  69. pushHandler = createPushHandler(service);
  70. atmosphere = getPreInitializedAtmosphere(vaadinServletConfig);
  71. if (atmosphere == null) {
  72. // Not initialized by JSR356WebsocketInitializer
  73. getLogger().fine(
  74. "Initializing Atmosphere for servlet "
  75. + vaadinServletConfig.getServletName());
  76. try {
  77. atmosphere = initAtmosphere(vaadinServletConfig);
  78. } catch (Exception e) {
  79. getLogger().log(
  80. Level.WARNING,
  81. "Failed to initialize Atmosphere for "
  82. + service.getServlet().getServletName()
  83. + ". Push will not work.", e);
  84. return;
  85. }
  86. } else {
  87. getLogger().fine(
  88. "Using pre-initialized Atmosphere for servlet "
  89. + vaadinServletConfig.getServletName());
  90. }
  91. pushHandler
  92. .setLongPollingSuspendTimeout(atmosphere
  93. .getAtmosphereConfig()
  94. .getInitParameter(
  95. com.vaadin.server.Constants.SERVLET_PARAMETER_PUSH_SUSPEND_TIMEOUT_LONGPOLLING,
  96. -1));
  97. for (AtmosphereHandlerWrapper handlerWrapper : atmosphere
  98. .getAtmosphereHandlers().values()) {
  99. AtmosphereHandler handler = handlerWrapper.atmosphereHandler;
  100. if (handler instanceof PushAtmosphereHandler) {
  101. // Map the (possibly pre-initialized) handler to the actual push
  102. // handler
  103. ((PushAtmosphereHandler) handler).setPushHandler(pushHandler);
  104. }
  105. }
  106. }
  107. /**
  108. * Creates a push handler for this request handler.
  109. * <p>
  110. * Create your own request handler and override this method if you want to
  111. * customize the {@link PushHandler}, e.g. to dynamically decide the suspend
  112. * timeout.
  113. *
  114. * @since
  115. * @param service
  116. * the vaadin service
  117. * @return the push handler to use for this service
  118. */
  119. protected PushHandler createPushHandler(VaadinServletService service) {
  120. return new PushHandler(service);
  121. }
  122. private static final Logger getLogger() {
  123. return Logger.getLogger(PushRequestHandler.class.getName());
  124. }
  125. /**
  126. * Returns an AtmosphereFramework instance which was initialized in the
  127. * servlet context init phase by {@link JSR356WebsocketInitializer}, if such
  128. * exists
  129. */
  130. private AtmosphereFramework getPreInitializedAtmosphere(
  131. ServletConfig vaadinServletConfig) {
  132. String attributeName = JSR356WebsocketInitializer
  133. .getAttributeName(vaadinServletConfig.getServletName());
  134. Object framework = vaadinServletConfig.getServletContext()
  135. .getAttribute(attributeName);
  136. if (framework != null && framework instanceof AtmosphereFramework) {
  137. return (AtmosphereFramework) framework;
  138. }
  139. return null;
  140. }
  141. /**
  142. * Initializes Atmosphere for the given ServletConfiguration
  143. *
  144. * @since 7.5.0
  145. * @param vaadinServletConfig
  146. * The servlet configuration for the servlet which should have
  147. * Atmosphere support
  148. */
  149. static AtmosphereFramework initAtmosphere(
  150. final ServletConfig vaadinServletConfig) {
  151. AtmosphereFramework atmosphere = new AtmosphereFramework(false, false) {
  152. @Override
  153. protected void analytics() {
  154. // Overridden to disable version number check
  155. }
  156. @Override
  157. public AtmosphereFramework addInitParameter(String name,
  158. String value) {
  159. if (vaadinServletConfig.getInitParameter(name) == null) {
  160. super.addInitParameter(name, value);
  161. }
  162. return this;
  163. }
  164. };
  165. atmosphere.addAtmosphereHandler("/*", new PushAtmosphereHandler());
  166. atmosphere.addInitParameter(ApplicationConfig.BROADCASTER_CACHE,
  167. UUIDBroadcasterCache.class.getName());
  168. atmosphere.addInitParameter(ApplicationConfig.ANNOTATION_PROCESSOR,
  169. VoidAnnotationProcessor.class.getName());
  170. atmosphere.addInitParameter(ApplicationConfig.PROPERTY_SESSION_SUPPORT,
  171. "true");
  172. atmosphere.addInitParameter(ApplicationConfig.MESSAGE_DELIMITER,
  173. String.valueOf(PushConstants.MESSAGE_DELIMITER));
  174. atmosphere.addInitParameter(
  175. ApplicationConfig.DROP_ACCESS_CONTROL_ALLOW_ORIGIN_HEADER,
  176. "false");
  177. // Disable heartbeat (it does not emit correct events client side)
  178. // https://github.com/Atmosphere/atmosphere-javascript/issues/141
  179. atmosphere.addInitParameter(
  180. ApplicationConfig.DISABLE_ATMOSPHEREINTERCEPTORS,
  181. HeartbeatInterceptor.class.getName());
  182. final String bufferSize = String
  183. .valueOf(PushConstants.WEBSOCKET_BUFFER_SIZE);
  184. atmosphere.addInitParameter(ApplicationConfig.WEBSOCKET_BUFFER_SIZE,
  185. bufferSize);
  186. atmosphere.addInitParameter(ApplicationConfig.WEBSOCKET_MAXTEXTSIZE,
  187. bufferSize);
  188. atmosphere.addInitParameter(ApplicationConfig.WEBSOCKET_MAXBINARYSIZE,
  189. bufferSize);
  190. atmosphere.addInitParameter(
  191. ApplicationConfig.PROPERTY_ALLOW_SESSION_TIMEOUT_REMOVAL,
  192. "false");
  193. // Disable Atmosphere's message about commercial support
  194. atmosphere.addInitParameter("org.atmosphere.cpr.showSupportMessage",
  195. "false");
  196. try {
  197. atmosphere.init(vaadinServletConfig);
  198. // Ensure the client-side knows how to split the message stream
  199. // into individual messages when using certain transports
  200. AtmosphereInterceptor trackMessageSize = new TrackMessageSizeInterceptor();
  201. trackMessageSize.configure(atmosphere.getAtmosphereConfig());
  202. atmosphere.interceptor(trackMessageSize);
  203. } catch (ServletException e) {
  204. throw new RuntimeException("Atmosphere init failed", e);
  205. }
  206. return atmosphere;
  207. }
  208. @Override
  209. public boolean handleRequest(VaadinSession session, VaadinRequest request,
  210. VaadinResponse response) throws IOException {
  211. if (!ServletPortletHelper.isPushRequest(request)) {
  212. return false;
  213. }
  214. if (request instanceof VaadinServletRequest) {
  215. if (atmosphere == null) {
  216. response.sendError(500,
  217. "Atmosphere initialization failed. No push available.");
  218. return true;
  219. }
  220. try {
  221. atmosphere.doCometSupport(AtmosphereRequest
  222. .wrap((VaadinServletRequest) request),
  223. AtmosphereResponse
  224. .wrap((VaadinServletResponse) response));
  225. } catch (ServletException e) {
  226. // TODO PUSH decide how to handle
  227. throw new RuntimeException(e);
  228. }
  229. } else {
  230. throw new IllegalArgumentException(
  231. "Portlets not currently supported");
  232. }
  233. return true;
  234. }
  235. public void destroy() {
  236. atmosphere.destroy();
  237. }
  238. /*
  239. * (non-Javadoc)
  240. *
  241. * @see
  242. * com.vaadin.server.SessionExpiredHandler#handleSessionExpired(com.vaadin
  243. * .server.VaadinRequest, com.vaadin.server.VaadinResponse)
  244. */
  245. @Override
  246. public boolean handleSessionExpired(VaadinRequest request,
  247. VaadinResponse response) throws IOException {
  248. // Websockets request must be handled by accepting the websocket
  249. // connection and then sending session expired so we let
  250. // PushRequestHandler handle it
  251. return handleRequest(null, request, response);
  252. }
  253. }