@@ -15,13 +15,17 @@ | |||
*/ | |||
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; | |||
@@ -46,6 +50,9 @@ import com.vaadin.server.VaadinServlet; | |||
*/ | |||
@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!"; | |||
@@ -78,10 +85,19 @@ public class VaadinServletRegistration { | |||
Boolean.toString(annotation.asyncSupported())); | |||
} | |||
bundleContext.registerService(Servlet.class, servlet, properties); | |||
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)) { | |||
@@ -112,8 +128,13 @@ public class VaadinServletRegistration { | |||
} | |||
} | |||
void unbindVaadinServlet(VaadinServlet servlet) { | |||
void unbindVaadinServlet(ServiceReference<VaadinServlet> servletRef) { | |||
Long serviceId = getServiceId(servletRef); | |||
ServiceRegistration<?> servletRegistration = registeredServlets | |||
.remove(serviceId); | |||
if (servletRegistration != null) { | |||
servletRegistration.unregister(); | |||
} | |||
} | |||
@Reference(cardinality = ReferenceCardinality.OPTIONAL) |
@@ -0,0 +1,30 @@ | |||
/* | |||
* 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.resources; | |||
/** | |||
* Used to declare a Vaadin Theme for use in OSGi. The theme is expected to be | |||
* in the same OSGi bundle as the class implementing this interface, under the | |||
* path "/VAADIN/themes/{themeName}" where {themeName} is what is returned by | |||
* {@link OSGiVaadinTheme#getName()}. | |||
* | |||
* @author Vaadin Ltd. | |||
* | |||
* @since 8.1 | |||
*/ | |||
public interface OSGiVaadinTheme { | |||
public String getName(); | |||
} |
@@ -0,0 +1,30 @@ | |||
/* | |||
* 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.resources; | |||
/** | |||
* Used to declare a Vaadin Widgetset for use in OSGi. The widgetset is expected | |||
* to be in the same OSGi bundle as the class implementing this interface, under | |||
* the path "/VAADIN/widgetsets/{widgetsetName}" where {widgetsetName} is what | |||
* is returned by {@link OSGiVaadinWidgetset#getName()}. | |||
* | |||
* @author Vaadin Ltd. | |||
* | |||
* @since 8.1 | |||
*/ | |||
public interface OSGiVaadinWidgetset { | |||
public String getName(); | |||
} |
@@ -0,0 +1,118 @@ | |||
/* | |||
* 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.resources.impl; | |||
/** | |||
* Helper for formatting the Alias, and Theme and Widgetset names. | |||
* | |||
* @author Vaadin Ltd. | |||
* | |||
* @since 8.1 | |||
*/ | |||
public final class PathFormatHelper { | |||
private static final String VAADIN_ROOT_ALIAS_FORMAT = "/%s/VAADIN/%s"; | |||
private static final String VAADIN_ROOT_FORMAT = "/VAADIN/%s"; | |||
private static final String VAADIN_THEME_ALIAS_FORMAT = "/%s/VAADIN/themes/%s"; | |||
private static final String VAADIN_WIDGETSET_ALIAS_FORMAT = "/%s/VAADIN/widgetsets/%s"; | |||
private static final String VAADIN_THEME_PATH_FORMAT = "/VAADIN/themes/%s"; | |||
private static final String VAADIN_WIDGETSET_PATH_FORMAT = "/VAADIN/widgetsets/%s"; | |||
private PathFormatHelper() { | |||
} | |||
/** | |||
* Returns the alias for the theme given a the theme name and a path prefix. | |||
* | |||
* @param themeName | |||
* the theme name | |||
* @param pathPrefix | |||
* the prefix for the /VAADIN/ folder | |||
* @return the alias | |||
*/ | |||
public static String getThemeAlias(String themeName, String pathPrefix) { | |||
return String.format(VAADIN_THEME_ALIAS_FORMAT, pathPrefix, themeName); | |||
} | |||
/** | |||
* Returns the expected/default path of the theme folder in the source | |||
* bundle. | |||
* | |||
* @param themeName | |||
* the name of the theme | |||
* @return the path of the theme folder in the source bundle | |||
*/ | |||
public static String getThemePath(String themeName) { | |||
return String.format(VAADIN_THEME_PATH_FORMAT, themeName); | |||
} | |||
/** | |||
* Returns the alias for a widgetset given a the widgetset name and a path | |||
* prefix. | |||
* | |||
* @param widgetsetName | |||
* the name of the widgetset | |||
* @param pathPrefix | |||
* the prefix for the /VAADIN/ folder | |||
* @return the alias | |||
*/ | |||
public static String getWidgetsetAlias(String widgetsetName, | |||
String pathPrefix) { | |||
return String.format(VAADIN_WIDGETSET_ALIAS_FORMAT, pathPrefix, | |||
widgetsetName); | |||
} | |||
/** | |||
* Returns the expected/default path of the widgetset folder in the source | |||
* bundle. | |||
* | |||
* @param widgetsetName | |||
* the name of the widgetset | |||
* @return the path of the widgetset folder in the source bundle | |||
*/ | |||
public static String getWidgetsetPath(String widgetsetName) { | |||
return String.format(VAADIN_WIDGETSET_PATH_FORMAT, widgetsetName); | |||
} | |||
/** | |||
* Returns the alias for a resource that will placed under the /VAADIN/ | |||
* folder. | |||
* | |||
* @param resourceName | |||
* the name of the resource | |||
* @param pathPrefix | |||
* the prefix for the /VAADIN/ folder | |||
* @return the alias | |||
*/ | |||
public static String getRootResourceAlias(String resourceName, | |||
String pathPrefix) { | |||
return String.format(VAADIN_ROOT_ALIAS_FORMAT, pathPrefix, | |||
resourceName); | |||
} | |||
/** | |||
* Returns the expected/default path of the resource in the source bundle. | |||
* | |||
* @param resourceName | |||
* the name of the resource | |||
* @return the path of the resource in the source bundle | |||
*/ | |||
public static String getRootResourcePath(String resourceName) { | |||
return String.format(VAADIN_ROOT_FORMAT, resourceName); | |||
} | |||
} |
@@ -23,71 +23,58 @@ import com.vaadin.osgi.resources.VaadinResourceService; | |||
/** | |||
* Implementation of {@link VaadinResourceService}. Uses bundle version as a | |||
* prefix for the /VAADIN/ folder. | |||
* | |||
* | |||
* @author Vaadin Ltd. | |||
* | |||
* | |||
* @since 8.1 | |||
*/ | |||
public class VaadinResourceServiceImpl implements VaadinResourceService { | |||
private static final String NAMESPACE_PREFIX = "vaadin-%s"; | |||
private static final String VAADIN_ROOT_ALIAS_FORMAT = "/%s/VAADIN/%s"; | |||
private static final String VAADIN_ROOT_FORMAT = "/VAADIN/%s"; | |||
private static final String VAADIN_THEME_ALIAS_FORMAT = "/%s/VAADIN/themes/%s"; | |||
private static final String VAADIN_WIDGETSET_ALIAS_FORMAT = "/%s/VAADIN/widgetsets/%s"; | |||
private static final String VAADIN_THEME_PATH_FORMAT = "/VAADIN/themes/%s"; | |||
private static final String VAADIN_WIDGETSET_PATH_FORMAT = "/VAADIN/widgetsets/%s"; | |||
private String bundleVersion; | |||
private String pathPrefix; | |||
/** | |||
* Sets the version of the bundle managing this service. | |||
* | |||
* | |||
* <p> | |||
* This needs to be called before any other method after the service is | |||
* created. | |||
* | |||
* | |||
* @param bundleVersion | |||
* the version of the bundle managing this service | |||
*/ | |||
public void setBundleVersion(String bundleVersion) { | |||
this.bundleVersion = bundleVersion; | |||
pathPrefix = String.format(NAMESPACE_PREFIX, bundleVersion); | |||
} | |||
@Override | |||
public void publishTheme(String themeName, HttpService httpService) | |||
throws NamespaceException { | |||
doPublish(themeName, VAADIN_THEME_ALIAS_FORMAT, | |||
VAADIN_THEME_PATH_FORMAT, httpService); | |||
} | |||
private void doPublish(String resourceName, String aliasFormat, | |||
String pathFormat, HttpService httpService) | |||
throws NamespaceException { | |||
String bundleVersionPrefix = String.format(NAMESPACE_PREFIX, | |||
bundleVersion); | |||
String resourcePath = String.format(pathFormat, resourceName); | |||
String resourceAlias = String.format(aliasFormat, bundleVersionPrefix, | |||
resourceName); | |||
httpService.registerResources(resourceAlias, resourcePath, null); | |||
String themeAlias = PathFormatHelper.getThemeAlias(themeName, | |||
pathPrefix); | |||
String themePath = PathFormatHelper.getThemePath(themeName); | |||
httpService.registerResources(themeAlias, themePath, null); | |||
} | |||
@Override | |||
public void publishResource(String resource, HttpService httpService) | |||
throws NamespaceException { | |||
doPublish(resource, VAADIN_ROOT_ALIAS_FORMAT, VAADIN_ROOT_FORMAT, | |||
httpService); | |||
String alias = PathFormatHelper.getRootResourceAlias(resource, | |||
pathPrefix); | |||
String path = PathFormatHelper.getRootResourcePath(resource); | |||
httpService.registerResources(alias, path, null); | |||
} | |||
@Override | |||
public void publishWidgetset(String widgetset, HttpService httpService) | |||
throws NamespaceException { | |||
doPublish(widgetset, VAADIN_WIDGETSET_ALIAS_FORMAT, | |||
VAADIN_WIDGETSET_PATH_FORMAT, httpService); | |||
String widgetsetAlias = PathFormatHelper.getWidgetsetAlias(widgetset, | |||
pathPrefix); | |||
String widgetsetPath = PathFormatHelper.getWidgetsetPath(widgetset); | |||
httpService.registerResources(widgetsetAlias, widgetsetPath, null); | |||
} | |||
@Override |
@@ -0,0 +1,175 @@ | |||
/* | |||
* 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.resources.impl; | |||
import java.io.IOException; | |||
import java.net.URL; | |||
import java.util.Collections; | |||
import java.util.LinkedHashMap; | |||
import java.util.Map; | |||
import javax.servlet.http.HttpServletRequest; | |||
import javax.servlet.http.HttpServletResponse; | |||
import org.osgi.framework.Bundle; | |||
import org.osgi.framework.BundleContext; | |||
import org.osgi.framework.Constants; | |||
import org.osgi.framework.ServiceReference; | |||
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.HttpContext; | |||
import org.osgi.service.http.HttpService; | |||
import org.osgi.service.http.NamespaceException; | |||
import com.vaadin.osgi.resources.OSGiVaadinResources; | |||
import com.vaadin.osgi.resources.OSGiVaadinResources.ResourceBundleInactiveException; | |||
import com.vaadin.osgi.resources.OSGiVaadinTheme; | |||
import com.vaadin.osgi.resources.OSGiVaadinWidgetset; | |||
import com.vaadin.osgi.resources.VaadinResourceService; | |||
/** | |||
* Tracks {@link OSGiVaadinWidgetset} and {@link OSGiVaadinTheme} registration | |||
* and uses {@link HttpService} to register them. | |||
* | |||
* @author Vaadin Ltd. | |||
* | |||
* @since 8.1 | |||
*/ | |||
@Component(immediate = true) | |||
public class VaadinResourceTrackerComponent { | |||
private HttpService httpService; | |||
private Map<Long, String> themeToAlias = Collections | |||
.synchronizedMap(new LinkedHashMap<>()); | |||
private Map<Long, String> widgetsetToAlias = Collections | |||
.synchronizedMap(new LinkedHashMap<>()); | |||
@Reference(cardinality = ReferenceCardinality.MULTIPLE, service = OSGiVaadinTheme.class, policy = ReferencePolicy.DYNAMIC) | |||
void bindTheme(ServiceReference<OSGiVaadinTheme> themeRef) | |||
throws ResourceBundleInactiveException, NamespaceException { | |||
Bundle bundle = themeRef.getBundle(); | |||
BundleContext context = bundle.getBundleContext(); | |||
OSGiVaadinTheme theme = context.getService(themeRef); | |||
if (theme == null) | |||
return; | |||
VaadinResourceService resourceService = OSGiVaadinResources | |||
.getService(); | |||
try { | |||
String pathPrefix = resourceService.getResourcePathPrefix(); | |||
Long serviceId = (Long) themeRef.getProperty(Constants.SERVICE_ID); | |||
String alias = PathFormatHelper.getThemeAlias(theme.getName(), | |||
pathPrefix); | |||
String path = PathFormatHelper.getThemePath(theme.getName()); | |||
httpService.registerResources(alias, path, | |||
new Delegate(httpService, bundle)); | |||
themeToAlias.put(serviceId, alias); | |||
} finally { | |||
context.ungetService(themeRef); | |||
} | |||
} | |||
void unbindTheme(ServiceReference<OSGiVaadinTheme> themeRef) { | |||
Long serviceId = (Long) themeRef.getProperty(Constants.SERVICE_ID); | |||
String themeAlias = themeToAlias.remove(serviceId); | |||
if (themeAlias != null && httpService != null) { | |||
httpService.unregister(themeAlias); | |||
} | |||
} | |||
@Reference(cardinality = ReferenceCardinality.MULTIPLE, service = OSGiVaadinWidgetset.class, policy = ReferencePolicy.DYNAMIC) | |||
void bindWidgetset(ServiceReference<OSGiVaadinWidgetset> widgetsetRef) | |||
throws ResourceBundleInactiveException, NamespaceException { | |||
Bundle bundle = widgetsetRef.getBundle(); | |||
BundleContext context = bundle.getBundleContext(); | |||
OSGiVaadinWidgetset widgetset = context.getService(widgetsetRef); | |||
if (widgetset == null) | |||
return; | |||
VaadinResourceService service = OSGiVaadinResources.getService(); | |||
try { | |||
String pathPrefix = service.getResourcePathPrefix(); | |||
Long serviceId = (Long) widgetsetRef | |||
.getProperty(Constants.SERVICE_ID); | |||
String alias = PathFormatHelper | |||
.getWidgetsetAlias(widgetset.getName(), pathPrefix); | |||
String path = PathFormatHelper | |||
.getWidgetsetPath(widgetset.getName()); | |||
httpService.registerResources(alias, path, | |||
new Delegate(httpService, bundle)); | |||
widgetsetToAlias.put(serviceId, alias); | |||
} finally { | |||
context.ungetService(widgetsetRef); | |||
} | |||
} | |||
void unbindWidgetset(ServiceReference<OSGiVaadinWidgetset> widgetsetRef) { | |||
Long serviceId = (Long) widgetsetRef.getProperty(Constants.SERVICE_ID); | |||
String widgetsetAlias = widgetsetToAlias.remove(serviceId); | |||
if (widgetsetAlias != null && httpService != null) { | |||
httpService.unregister(widgetsetAlias); | |||
} | |||
} | |||
@Reference | |||
void setHttpService(HttpService service) { | |||
this.httpService = service; | |||
} | |||
void unsetHttpService(HttpService service) { | |||
this.httpService = null; | |||
} | |||
static final class Delegate implements HttpContext { | |||
private HttpContext context; | |||
private Bundle bundle; | |||
public Delegate(HttpService service, Bundle bundle) { | |||
this.context = service.createDefaultHttpContext(); | |||
this.bundle = bundle; | |||
} | |||
@Override | |||
public boolean handleSecurity(HttpServletRequest request, | |||
HttpServletResponse response) throws IOException { | |||
return context.handleSecurity(request, response); | |||
} | |||
@Override | |||
public URL getResource(String name) { | |||
return bundle.getResource(name); | |||
} | |||
@Override | |||
public String getMimeType(String name) { | |||
return context.getMimeType(name); | |||
} | |||
} | |||
} |