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.

VaadinResourceTrackerComponent.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  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.resources.impl;
  17. import java.util.ArrayList;
  18. import java.util.Collections;
  19. import java.util.Dictionary;
  20. import java.util.Hashtable;
  21. import java.util.LinkedHashMap;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Objects;
  25. import org.osgi.framework.Bundle;
  26. import org.osgi.framework.BundleContext;
  27. import org.osgi.framework.Constants;
  28. import org.osgi.framework.ServiceReference;
  29. import org.osgi.framework.ServiceRegistration;
  30. import org.osgi.service.component.annotations.Activate;
  31. import org.osgi.service.component.annotations.Component;
  32. import org.osgi.service.component.annotations.Deactivate;
  33. import org.osgi.service.component.annotations.Reference;
  34. import org.osgi.service.component.annotations.ReferenceCardinality;
  35. import org.osgi.service.component.annotations.ReferencePolicy;
  36. import org.osgi.service.http.HttpService;
  37. import org.osgi.service.http.whiteboard.HttpWhiteboardConstants;
  38. import com.vaadin.osgi.resources.OsgiVaadinContributor;
  39. import com.vaadin.osgi.resources.OsgiVaadinResource;
  40. import com.vaadin.osgi.resources.OsgiVaadinTheme;
  41. import com.vaadin.osgi.resources.OsgiVaadinWidgetset;
  42. import com.vaadin.osgi.resources.VaadinResourceService;
  43. /**
  44. * Tracks {@link OsgiVaadinWidgetset} and {@link OsgiVaadinTheme} registration
  45. * and uses {@link HttpService} to register them.
  46. *
  47. * @author Vaadin Ltd.
  48. *
  49. * @since 8.1
  50. */
  51. @Component(immediate = true)
  52. public class VaadinResourceTrackerComponent {
  53. private final Map<Long, Delegate<?>> resourceToRegistration = Collections
  54. .synchronizedMap(new LinkedHashMap<>());
  55. private final Map<Long, List<ServiceRegistration<? extends OsgiVaadinResource>>> contributorToRegistrations = Collections
  56. .synchronizedMap(new LinkedHashMap<>());
  57. private BundleContext vaadinSharedContext;
  58. private VaadinResourceService vaadinService;
  59. @Reference(cardinality = ReferenceCardinality.MULTIPLE, service = OsgiVaadinTheme.class, policy = ReferencePolicy.DYNAMIC)
  60. void bindTheme(ServiceReference<OsgiVaadinTheme> themeRef) {
  61. registerResource(themeRef);
  62. }
  63. void unbindTheme(ServiceReference<OsgiVaadinTheme> themeRef) {
  64. unregisterResource(themeRef);
  65. }
  66. @Reference(cardinality = ReferenceCardinality.MULTIPLE, service = OsgiVaadinWidgetset.class, policy = ReferencePolicy.DYNAMIC)
  67. void bindWidgetset(ServiceReference<OsgiVaadinWidgetset> widgetsetRef) {
  68. registerResource(widgetsetRef);
  69. }
  70. void unbindWidgetset(ServiceReference<OsgiVaadinWidgetset> widgetsetRef) {
  71. unregisterResource(widgetsetRef);
  72. }
  73. @Reference(cardinality = ReferenceCardinality.MULTIPLE, service = OsgiVaadinResource.class, policy = ReferencePolicy.DYNAMIC)
  74. void bindResource(ServiceReference<OsgiVaadinResource> resourceRef) {
  75. registerResource(resourceRef);
  76. }
  77. void unbindResource(ServiceReference<OsgiVaadinResource> resourceRef) {
  78. unregisterResource(resourceRef);
  79. }
  80. @Reference(cardinality = ReferenceCardinality.MULTIPLE, service = OsgiVaadinContributor.class, policy = ReferencePolicy.DYNAMIC)
  81. void bindContributor(
  82. ServiceReference<OsgiVaadinContributor> contributorRef) {
  83. Bundle bundle = contributorRef.getBundle();
  84. BundleContext context = bundle.getBundleContext();
  85. OsgiVaadinContributor contributor = context.getService(contributorRef);
  86. if (contributor == null) {
  87. return;
  88. }
  89. Long serviceId = (Long) contributorRef
  90. .getProperty(Constants.SERVICE_ID);
  91. List<OsgiVaadinResource> contributions = contributor.getContributions();
  92. List<ServiceRegistration<? extends OsgiVaadinResource>> registrations = new ArrayList<>(
  93. contributions.size());
  94. for (final OsgiVaadinResource r : contributions) {
  95. ServiceRegistration<? extends OsgiVaadinResource> reg;
  96. if (r instanceof OsgiVaadinTheme) {
  97. reg = context.registerService(OsgiVaadinTheme.class,
  98. (OsgiVaadinTheme) r, null);
  99. } else if (r instanceof OsgiVaadinWidgetset) {
  100. reg = context.registerService(OsgiVaadinWidgetset.class,
  101. (OsgiVaadinWidgetset) r, null);
  102. } else {
  103. reg = context.registerService(OsgiVaadinResource.class, r,
  104. null);
  105. }
  106. registrations.add(reg);
  107. }
  108. contributorToRegistrations.put(serviceId, registrations);
  109. }
  110. void unbindContributor(
  111. ServiceReference<OsgiVaadinContributor> contributorRef) {
  112. Long serviceId = (Long) contributorRef
  113. .getProperty(Constants.SERVICE_ID);
  114. List<ServiceRegistration<? extends OsgiVaadinResource>> registrations = contributorToRegistrations
  115. .remove(serviceId);
  116. if (registrations != null) {
  117. for (ServiceRegistration<? extends OsgiVaadinResource> reg : registrations) {
  118. reg.unregister();
  119. }
  120. }
  121. }
  122. @Reference
  123. void bindVaadinResourceService(VaadinResourceService vaadinService) {
  124. this.vaadinService = vaadinService;
  125. }
  126. void unbindVaadinResourceService(VaadinResourceService vaadinService) {
  127. if (this.vaadinService == vaadinService) {
  128. this.vaadinService = null;
  129. }
  130. }
  131. /**
  132. *
  133. * @since 8.6.0
  134. */
  135. @Activate
  136. protected void activate(BundleContext context) {
  137. vaadinSharedContext = context;
  138. for (Delegate<?> registration : resourceToRegistration.values()) {
  139. registration.register(vaadinSharedContext, vaadinService);
  140. }
  141. }
  142. /**
  143. * @since 8.6.0
  144. */
  145. @Deactivate
  146. protected void deactivate() {
  147. for (final Delegate<?> registration : resourceToRegistration.values()) {
  148. unregisterResource(registration);
  149. }
  150. for (List<ServiceRegistration<? extends OsgiVaadinResource>> registrations : contributorToRegistrations
  151. .values()) {
  152. for (ServiceRegistration<? extends OsgiVaadinResource> reg : registrations) {
  153. reg.unregister();
  154. }
  155. }
  156. resourceToRegistration.clear();
  157. contributorToRegistrations.clear();
  158. vaadinSharedContext = null;
  159. vaadinService = null;
  160. }
  161. private <T extends OsgiVaadinResource> void registerResource(
  162. ServiceReference<T> resourceRef) {
  163. String pattern = (String) resourceRef.getProperty(
  164. HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN);
  165. // if this resource contains a http whiteboard property we are done here
  166. // because we are registering the same service with whiteboard
  167. // properties we have to filter them here
  168. if (pattern != null)
  169. return;
  170. BundleContext context = resourceRef.getBundle().getBundleContext();
  171. Long serviceId = (Long) resourceRef.getProperty(Constants.SERVICE_ID);
  172. Delegate<T> registration = new Delegate<>(resourceRef, context);
  173. resourceToRegistration.put(serviceId, registration);
  174. registration.register(vaadinSharedContext, vaadinService);
  175. }
  176. private void unregisterResource(
  177. ServiceReference<? extends OsgiVaadinResource> resourceRef) {
  178. Long serviceId = (Long) resourceRef.getProperty(Constants.SERVICE_ID);
  179. unregisterResource(serviceId);
  180. }
  181. private void unregisterResource(Long serviceId) {
  182. if (serviceId == null)
  183. return;
  184. Delegate<?> registration = resourceToRegistration.remove(serviceId);
  185. unregisterResource(registration);
  186. }
  187. private void unregisterResource(Delegate<?> registration) {
  188. if (registration != null) {
  189. registration.unregister();
  190. }
  191. }
  192. static final class Delegate<T extends OsgiVaadinResource> {
  193. private final ServiceReference<T> resourceRef;
  194. // the bundle context who contributed the resource - we reuse that so we
  195. // can register the http whiteboard resource in the name of the
  196. // contributing bundle
  197. private final BundleContext bundleContext;
  198. private volatile BundleContext vaadinSharedContext;
  199. private volatile VaadinResourceService vaadinService;
  200. private volatile ServiceRegistration<? super T> resourceRegistration;
  201. public Delegate(ServiceReference<T> resourceRef,
  202. BundleContext bundleContext) {
  203. this.resourceRef = Objects.requireNonNull(resourceRef);
  204. this.bundleContext = Objects.requireNonNull(bundleContext);
  205. }
  206. public void register(BundleContext vaadinSharedContext,
  207. VaadinResourceService vaadinService) {
  208. if (vaadinService != null) {
  209. this.vaadinService = vaadinService;
  210. }
  211. if (vaadinSharedContext != null) {
  212. this.vaadinSharedContext = vaadinSharedContext;
  213. }
  214. // if all dependencies are satisfied we can finally register the
  215. // http resource
  216. if (this.vaadinService != null
  217. && this.vaadinSharedContext != null) {
  218. this.registerImpl();
  219. }
  220. }
  221. public void unregister() {
  222. if (resourceRegistration != null) {
  223. resourceRegistration.unregister();
  224. }
  225. if (vaadinSharedContext != null) {
  226. // unget the service reference
  227. vaadinSharedContext.ungetService(resourceRef);
  228. }
  229. vaadinService = null;
  230. vaadinSharedContext = null;
  231. resourceRegistration = null;
  232. }
  233. @SuppressWarnings("unchecked")
  234. private void registerImpl() {
  235. // we have already registered if resourceRegistration is set
  236. if (resourceRegistration != null)
  237. return;
  238. T resource = vaadinSharedContext.getService(this.resourceRef);
  239. // we don't need a path prefix because we register at the vaadin
  240. // context which handles the prefixing
  241. String pathPrefix = "";
  242. Class<? super T> interfaceType;
  243. String alias;
  244. String path;
  245. if (resource instanceof OsgiVaadinWidgetset) {
  246. alias = PathFormatHelper.getWidgetsetAlias(resource.getName(),
  247. pathPrefix);
  248. // OsgiVaadinWidgetset provides folders so we have to add a
  249. // wildcard
  250. alias = alias + "/*";
  251. path = PathFormatHelper.getWidgetsetPath(resource.getName());
  252. // save cast because OsgiVaadinWidgetset is a super class of T
  253. interfaceType = (Class<? super T>) OsgiVaadinWidgetset.class;
  254. } else if (resource instanceof OsgiVaadinTheme) {
  255. alias = PathFormatHelper.getThemeAlias(resource.getName(),
  256. pathPrefix);
  257. // OsgiVaadinTheme provides folders so we have to add a wildcard
  258. alias = alias + "/*";
  259. path = PathFormatHelper.getThemePath(resource.getName());
  260. // save cast because OsgiVaadinTheme is a super class of T
  261. interfaceType = (Class<? super T>) OsgiVaadinTheme.class;
  262. } else {
  263. alias = PathFormatHelper
  264. .getRootResourceAlias(resource.getName(), pathPrefix);
  265. path = PathFormatHelper.getRootResourcePath(resource.getName());
  266. interfaceType = OsgiVaadinResource.class;
  267. }
  268. // remove the empty prefixed slash
  269. alias = alias.substring(1);
  270. final String contextFilter = "("
  271. + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "="
  272. + vaadinService.getContextName() + ")";
  273. // register a OSGi http resource based on the whiteboard pattern
  274. final Dictionary<String, String> properties = new Hashtable<>();
  275. properties.put(
  276. HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN,
  277. alias);
  278. properties.put(
  279. HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX,
  280. path);
  281. properties.put(
  282. HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,
  283. contextFilter);
  284. resourceRegistration = bundleContext.registerService(interfaceType,
  285. resource, properties);
  286. }
  287. }
  288. }