All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.osgi.servlet.VaadinServletRegistration Maven / Gradle / Ivy

/*
 * Copyright (C) 2000-2024 Vaadin Ltd
 *
 * This program is available under Vaadin Commercial License and Service Terms.
 *
 * See  for the full
 * license.
 */
package com.vaadin.osgi.servlet;

import java.util.Collections;
import java.util.Hashtable;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

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.Activate;
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.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
public class VaadinServletRegistration {
    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 final Map, ServletRegistration> registeredServlets = Collections
            .synchronizedMap(new LinkedHashMap<>());
    private VaadinResourceService vaadinService;
    private LogService logService;

    @Activate
    void activate(BundleContext bundleContext) throws Exception {
        // see if we have registrations already which are not initialized
        for(ServletRegistration registration : registeredServlets.values()) {
            registration.register(vaadinService);
        }
    }

    @Reference(cardinality = ReferenceCardinality.MULTIPLE, service = VaadinServlet.class, policy = ReferencePolicy.DYNAMIC)
    void bindVaadinServlet(VaadinServlet servlet,
            ServiceReference reference) {
        log(LogService.LOG_INFO, "VaadinServlet Registration");

        Hashtable properties = getProperties(reference);

        WebServlet annotation = servlet.getClass()
                .getAnnotation(WebServlet.class);

        if (!validateSettings(annotation, properties)) {
            return;
        }

        if (annotation != null) {
            properties.put(
                    HttpWhiteboardConstants.HTTP_WHITEBOARD_SERVLET_ASYNC_SUPPORTED,
                    Boolean.toString(annotation.asyncSupported()));
        }

        ServletRegistration registration = new ServletRegistration(servlet,
                reference, properties);
        registeredServlets.put(reference, registration);
        // try to register with the vaadin service - the service could be null at this point but we handle that in the register method
        registration.register(this.vaadinService);
    }

    private boolean validateSettings(WebServlet annotation,
            Hashtable 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 void log(int level, String message) {
        if (logService != null) {
            logService.log(level, message);
        }
    }

    void unbindVaadinServlet(ServiceReference reference) {
        ServletRegistration servletRegistration = registeredServlets
                .remove(reference);
        if (servletRegistration != null) {
            servletRegistration.unregister();
        }
    }

    @Reference
    void setVaadinResourceService(VaadinResourceService vaadinService) {
        this.vaadinService = vaadinService;
    }

    void unsetVaadinResourceService(VaadinResourceService vaadinService) {
        if (this.vaadinService == vaadinService) {
            this.vaadinService = null;
        }
    }

    @Reference(cardinality = ReferenceCardinality.OPTIONAL)
    void setLogService(LogService logService) {
        this.logService = logService;
    }

    void unsetLogService(LogService logService) {
        if (this.logService == logService) {
            this.logService = null;
        }
    }

    private Hashtable getProperties(
            ServiceReference reference) {
        Hashtable properties = new Hashtable<>();
        for (String key : reference.getPropertyKeys()) {
            properties.put(key, reference.getProperty(key));
        }
        return properties;
    }

    private static class ServletRegistration {
        private final VaadinServlet servlet;
        private final ServiceReference servletRef;
        private final Hashtable properties;
        
        private volatile ServiceRegistration registration;

        public ServletRegistration(VaadinServlet servlet,
                ServiceReference servletRef,
                Hashtable properties) {
            this.servlet = Objects.requireNonNull(servlet);
            this.servletRef = Objects.requireNonNull(servletRef);
            this.properties = properties;
        }

        public void register(VaadinResourceService vaadinService) {
            // we are already registered
            if (this.registration != null)
                return;
            // only register if the vaadin service is not null
            if(vaadinService == null)
                return;

            final String resourcePath = String.format("/%s", vaadinService.getResourcePathPrefix());
            this.properties.put(VAADIN_RESOURCES_PARAM, resourcePath);
            // We register the Http Whiteboard servlet using the context of
            // the bundle which registered the Vaadin Servlet, not our own
            BundleContext bundleContext = this.servletRef.getBundle()
                    .getBundleContext();
            this.registration = bundleContext.registerService(Servlet.class,
                    servlet, properties);
        }

        public void unregister() {
            //we are already deregistered
            if (this.registration == null)
                return;
            try {
                this.registration.unregister();
            } catch (IllegalStateException ise) {
                // This service may have already been unregistered
                // automatically by the OSGi framework if the
                // application bundle is being stopped. This is
                // obviously not a problem for us.
            }
            this.registration = null;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy