Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

PushRequestHandler.java 11KB

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