You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

VaadinServletRegistration.java 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /*
  2. * Copyright 2000-2018 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.osgi.servlet;
  17. import java.util.Collections;
  18. import java.util.Hashtable;
  19. import java.util.LinkedHashMap;
  20. import java.util.Map;
  21. import java.util.Objects;
  22. import javax.servlet.Servlet;
  23. import javax.servlet.annotation.WebServlet;
  24. import org.osgi.framework.BundleContext;
  25. import org.osgi.framework.ServiceReference;
  26. import org.osgi.framework.ServiceRegistration;
  27. import org.osgi.service.component.annotations.Activate;
  28. import org.osgi.service.component.annotations.Component;
  29. import org.osgi.service.component.annotations.Reference;
  30. import org.osgi.service.component.annotations.ReferenceCardinality;
  31. import org.osgi.service.component.annotations.ReferencePolicy;
  32. import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
  33. import org.osgi.service.log.LogService;
  34. import com.vaadin.osgi.resources.VaadinResourceService;
  35. import com.vaadin.server.Constants;
  36. import com.vaadin.server.VaadinServlet;
  37. /**
  38. * This component tracks {@link VaadinServlet} registrations, configures them
  39. * with the appropriate path to the Vaadin static resources and registers a
  40. * {@link Servlet} using the HttpWhiteboard specification.
  41. *
  42. * @author Vaadin Ltd.
  43. *
  44. * @since 8.1
  45. */
  46. @Component
  47. public class VaadinServletRegistration {
  48. private static final String MISSING_ANNOTATION_MESSAGE_FORMAT = "The property '%s' must be set in a '%s' without the '%s' annotation!";
  49. private static final String URL_PATTERNS_NOT_SET_MESSAGE_FORMAT = "The property '%s' must be set when the 'urlPatterns' attribute is not set!";
  50. private static final String SERVLET_PATTERN = HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN;
  51. private static final String VAADIN_RESOURCES_PARAM = HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX
  52. + Constants.PARAMETER_VAADIN_RESOURCES;
  53. private final Map<ServiceReference<VaadinServlet>, ServletRegistration> registeredServlets = Collections
  54. .synchronizedMap(new LinkedHashMap<>());
  55. private VaadinResourceService vaadinService;
  56. private LogService logService;
  57. @Activate
  58. void activate(BundleContext bundleContext) throws Exception {
  59. // see if we have registrations already which are not initialized
  60. for(ServletRegistration registration : registeredServlets.values()) {
  61. registration.register(vaadinService);
  62. }
  63. }
  64. @Reference(cardinality = ReferenceCardinality.MULTIPLE, service = VaadinServlet.class, policy = ReferencePolicy.DYNAMIC)
  65. void bindVaadinServlet(VaadinServlet servlet,
  66. ServiceReference<VaadinServlet> reference) {
  67. log(LogService.LOG_INFO, "VaadinServlet Registration");
  68. Hashtable<String, Object> properties = getProperties(reference);
  69. WebServlet annotation = servlet.getClass()
  70. .getAnnotation(WebServlet.class);
  71. if (!validateSettings(annotation, properties)) {
  72. return;
  73. }
  74. if (annotation != null) {
  75. properties.put(
  76. HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED,
  77. Boolean.toString(annotation.asyncSupported()));
  78. }
  79. ServletRegistration registration = new ServletRegistration(servlet,
  80. reference, properties);
  81. registeredServlets.put(reference, registration);
  82. // try to register with the vaadin service - the service could be null at this point but we handle that in the register method
  83. registration.register(this.vaadinService);
  84. }
  85. private boolean validateSettings(WebServlet annotation,
  86. Hashtable<String, Object> properties) {
  87. if (!properties.containsKey(SERVLET_PATTERN)) {
  88. if (annotation == null) {
  89. log(LogService.LOG_ERROR,
  90. String.format(MISSING_ANNOTATION_MESSAGE_FORMAT,
  91. SERVLET_PATTERN,
  92. VaadinServlet.class.getSimpleName(),
  93. WebServlet.class.getName()));
  94. return false;
  95. } else if (annotation.urlPatterns().length == 0) {
  96. log(LogService.LOG_ERROR, String.format(
  97. URL_PATTERNS_NOT_SET_MESSAGE_FORMAT, SERVLET_PATTERN));
  98. return false;
  99. }
  100. }
  101. return true;
  102. }
  103. private void log(int level, String message) {
  104. if (logService != null) {
  105. logService.log(level, message);
  106. }
  107. }
  108. void unbindVaadinServlet(ServiceReference<VaadinServlet> reference) {
  109. ServletRegistration servletRegistration = registeredServlets
  110. .remove(reference);
  111. if (servletRegistration != null) {
  112. servletRegistration.unregister();
  113. }
  114. }
  115. @Reference
  116. void setVaadinResourceService(VaadinResourceService vaadinService) {
  117. this.vaadinService = vaadinService;
  118. }
  119. void unsetVaadinResourceService(VaadinResourceService vaadinService) {
  120. if (this.vaadinService == vaadinService) {
  121. this.vaadinService = null;
  122. }
  123. }
  124. @Reference(cardinality = ReferenceCardinality.OPTIONAL)
  125. void setLogService(LogService logService) {
  126. this.logService = logService;
  127. }
  128. void unsetLogService(LogService logService) {
  129. if (this.logService == logService) {
  130. this.logService = null;
  131. }
  132. }
  133. private Hashtable<String, Object> getProperties(
  134. ServiceReference<VaadinServlet> reference) {
  135. Hashtable<String, Object> properties = new Hashtable<>();
  136. for (String key : reference.getPropertyKeys()) {
  137. properties.put(key, reference.getProperty(key));
  138. }
  139. return properties;
  140. }
  141. private static class ServletRegistration {
  142. private final VaadinServlet servlet;
  143. private final ServiceReference<VaadinServlet> servletRef;
  144. private final Hashtable<String, Object> properties;
  145. private volatile ServiceRegistration<Servlet> registration;
  146. public ServletRegistration(VaadinServlet servlet,
  147. ServiceReference<VaadinServlet> servletRef,
  148. Hashtable<String, Object> properties) {
  149. this.servlet = Objects.requireNonNull(servlet);
  150. this.servletRef = Objects.requireNonNull(servletRef);
  151. this.properties = properties;
  152. }
  153. public void register(VaadinResourceService vaadinService) {
  154. // we are already registered
  155. if (this.registration != null)
  156. return;
  157. // only register if the vaadin service is not null
  158. if(vaadinService == null)
  159. return;
  160. final String resourcePath = String.format("/%s", vaadinService.getResourcePathPrefix());
  161. this.properties.put(VAADIN_RESOURCES_PARAM, resourcePath);
  162. // We register the Http Whiteboard servlet using the context of
  163. // the bundle which registered the Vaadin Servlet, not our own
  164. BundleContext bundleContext = this.servletRef.getBundle()
  165. .getBundleContext();
  166. this.registration = bundleContext.registerService(Servlet.class,
  167. servlet, properties);
  168. }
  169. public void unregister() {
  170. //we are already deregistered
  171. if (this.registration == null)
  172. return;
  173. try {
  174. this.registration.unregister();
  175. } catch (IllegalStateException ise) {
  176. // This service may have already been unregistered
  177. // automatically by the OSGi framework if the
  178. // application bundle is being stopped. This is
  179. // obviously not a problem for us.
  180. }
  181. this.registration = null;
  182. }
  183. }
  184. }