/* * Copyright 2000-2016 Vaadin Ltd. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.vaadin.osgi.servlet; import java.util.Collections; import java.util.Hashtable; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.Servlet; import javax.servlet.annotation.WebServlet; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; import org.osgi.service.http.whiteboard.HttpWhiteboardConstants; import org.osgi.service.log.LogService; import com.vaadin.osgi.resources.OSGiVaadinResources; import com.vaadin.osgi.resources.OSGiVaadinResources.ResourceBundleInactiveException; import com.vaadin.osgi.resources.VaadinResourceService; import com.vaadin.server.Constants; import com.vaadin.server.VaadinServlet; /** * This component tracks {@link VaadinServlet} registrations, configures them * with the appropriate path to the Vaadin static resources and registers a * {@link Servlet} using the HttpWhiteboard specification. * * @author Vaadin Ltd. * * @since 8.1 */ @Component(immediate = true) public class VaadinServletRegistration { private Map<Long, ServiceRegistration<?>> registeredServlets = Collections .synchronizedMap(new LinkedHashMap<>()); private static final String MISSING_ANNOTATION_MESSAGE_FORMAT = "The property '%s' must be set in a '%s' without the '%s' annotation!"; private static final String URL_PATTERNS_NOT_SET_MESSAGE_FORMAT = "The property '%s' must be set when the 'urlPatterns' attribute is not set!"; private static final String SERVLET_PATTERN = HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_PATTERN; private static final String VAADIN_RESOURCES_PARAM = HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_INIT_PARAM_PREFIX + Constants.PARAMETER_VAADIN_RESOURCES; private LogService logService; @Reference(cardinality = ReferenceCardinality.MULTIPLE, service = VaadinServlet.class, policy = ReferencePolicy.DYNAMIC) void bindVaadinServlet(ServiceReference<VaadinServlet> reference) throws ResourceBundleInactiveException { log(LogService.LOG_WARNING, "VaadinServlet Registration"); BundleContext bundleContext = reference.getBundle().getBundleContext(); Hashtable<String, Object> properties = getProperties(reference); VaadinServlet servlet = bundleContext.getService(reference); WebServlet annotation = servlet.getClass() .getAnnotation(WebServlet.class); if (!validateSettings(annotation, properties)) return; properties.put(VAADIN_RESOURCES_PARAM, getResourcePath()); if (annotation != null) { properties.put( HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED, Boolean.toString(annotation.asyncSupported())); } ServiceRegistration<Servlet> servletRegistration = bundleContext .registerService(Servlet.class, servlet, properties); Long serviceId = getServiceId(reference); registeredServlets.put(serviceId, servletRegistration); bundleContext.ungetService(reference); } private Long getServiceId(ServiceReference<VaadinServlet> reference) { return (Long) reference .getProperty(org.osgi.framework.Constants.SERVICE_ID); } private boolean validateSettings(WebServlet annotation, Hashtable<String, Object> properties) { if (!properties.containsKey(SERVLET_PATTERN)) { if (annotation == null) { log(LogService.LOG_ERROR, String.format(MISSING_ANNOTATION_MESSAGE_FORMAT, SERVLET_PATTERN, VaadinServlet.class.getSimpleName(), WebServlet.class.getName())); return false; } else if (annotation.urlPatterns().length == 0) { log(LogService.LOG_ERROR, String.format( URL_PATTERNS_NOT_SET_MESSAGE_FORMAT, SERVLET_PATTERN)); return false; } } return true; } private String getResourcePath() throws ResourceBundleInactiveException { VaadinResourceService service = OSGiVaadinResources.getService(); return String.format("/%s", service.getResourcePathPrefix()); } private void log(int level, String message) { if (logService != null) { logService.log(level, message); } } void unbindVaadinServlet(ServiceReference<VaadinServlet> servletRef) { Long serviceId = getServiceId(servletRef); ServiceRegistration<?> servletRegistration = registeredServlets .remove(serviceId); if (servletRegistration != null) { servletRegistration.unregister(); } } @Reference(cardinality = ReferenceCardinality.OPTIONAL) void setLogService(LogService logService) { this.logService = logService; } void unsetLogService(LogService logService) { this.logService = null; } private Hashtable<String, Object> getProperties( ServiceReference<VaadinServlet> reference) { Hashtable<String, Object> properties = new Hashtable<>(); for (String key : reference.getPropertyKeys()) { properties.put(key, reference.getProperty(key)); } return properties; } }