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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. * Copyright 2000-2021 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. synchronized (resourceToRegistration) {
  138. vaadinSharedContext = context;
  139. for (Delegate<?> registration : resourceToRegistration.values()) {
  140. registration.register(context, vaadinService);
  141. }
  142. }
  143. }
  144. /**
  145. * @since 8.6.0
  146. */
  147. @Deactivate
  148. protected void deactivate() {
  149. for (final Delegate<?> registration : resourceToRegistration.values()) {
  150. unregisterResource(registration);
  151. }
  152. for (List<ServiceRegistration<? extends OsgiVaadinResource>> registrations : contributorToRegistrations
  153. .values()) {
  154. for (ServiceRegistration<? extends OsgiVaadinResource> reg : registrations) {
  155. reg.unregister();
  156. }
  157. }
  158. resourceToRegistration.clear();
  159. contributorToRegistrations.clear();
  160. vaadinSharedContext = null;
  161. vaadinService = null;
  162. }
  163. private <T extends OsgiVaadinResource> void registerResource(
  164. ServiceReference<T> resourceRef) {
  165. String pattern = (String) resourceRef.getProperty(
  166. HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN);
  167. // if this resource contains a http whiteboard property we are done here
  168. // because we are registering the same service with whiteboard
  169. // properties we have to filter them here
  170. if (pattern != null)
  171. return;
  172. BundleContext context = resourceRef.getBundle().getBundleContext();
  173. Long serviceId = (Long) resourceRef.getProperty(Constants.SERVICE_ID);
  174. Delegate<T> registration = new Delegate<>(resourceRef, context);
  175. resourceToRegistration.put(serviceId, registration);
  176. registration.register(vaadinSharedContext, vaadinService);
  177. }
  178. private void unregisterResource(
  179. ServiceReference<? extends OsgiVaadinResource> resourceRef) {
  180. Long serviceId = (Long) resourceRef.getProperty(Constants.SERVICE_ID);
  181. unregisterResource(serviceId);
  182. }
  183. private void unregisterResource(Long serviceId) {
  184. if (serviceId == null)
  185. return;
  186. Delegate<?> registration = resourceToRegistration.remove(serviceId);
  187. unregisterResource(registration);
  188. }
  189. private void unregisterResource(Delegate<?> registration) {
  190. if (registration != null) {
  191. registration.unregister();
  192. }
  193. }
  194. static final class Delegate<T extends OsgiVaadinResource> {
  195. private final ServiceReference<T> resourceRef;
  196. // the bundle context who contributed the resource - we reuse that so we
  197. // can register the http whiteboard resource in the name of the
  198. // contributing bundle
  199. private final BundleContext bundleContext;
  200. private volatile BundleContext vaadinSharedContext;
  201. private volatile VaadinResourceService vaadinService;
  202. private volatile ServiceRegistration<? super T> resourceRegistration;
  203. public Delegate(ServiceReference<T> resourceRef,
  204. BundleContext bundleContext) {
  205. this.resourceRef = Objects.requireNonNull(resourceRef);
  206. this.bundleContext = Objects.requireNonNull(bundleContext);
  207. }
  208. public void register(BundleContext vaadinSharedContext,
  209. VaadinResourceService vaadinService) {
  210. if (vaadinService != null) {
  211. this.vaadinService = vaadinService;
  212. }
  213. if (vaadinSharedContext != null) {
  214. this.vaadinSharedContext = vaadinSharedContext;
  215. }
  216. // if all dependencies are satisfied we can finally register the
  217. // http resource
  218. if (this.vaadinService != null
  219. && this.vaadinSharedContext != null) {
  220. this.registerImpl();
  221. }
  222. }
  223. public void unregister() {
  224. if (resourceRegistration != null) {
  225. resourceRegistration.unregister();
  226. }
  227. if (vaadinSharedContext != null) {
  228. // unget the service reference
  229. vaadinSharedContext.ungetService(resourceRef);
  230. }
  231. vaadinService = null;
  232. vaadinSharedContext = null;
  233. resourceRegistration = null;
  234. }
  235. @SuppressWarnings("unchecked")
  236. private void registerImpl() {
  237. // we have already registered if resourceRegistration is set
  238. if (resourceRegistration != null)
  239. return;
  240. T resource = vaadinSharedContext.getService(this.resourceRef);
  241. // we don't need a path prefix because we register at the vaadin
  242. // context which handles the prefixing
  243. String pathPrefix = "";
  244. Class<? super T> interfaceType;
  245. String alias;
  246. String path;
  247. if (resource instanceof OsgiVaadinWidgetset) {
  248. alias = PathFormatHelper.getWidgetsetAlias(resource.getName(),
  249. pathPrefix);
  250. // OsgiVaadinWidgetset provides folders so we have to add a
  251. // wildcard
  252. alias = alias + "/*";
  253. path = PathFormatHelper.getWidgetsetPath(resource.getName());
  254. // save cast because OsgiVaadinWidgetset is a super class of T
  255. interfaceType = (Class<? super T>) OsgiVaadinWidgetset.class;
  256. } else if (resource instanceof OsgiVaadinTheme) {
  257. alias = PathFormatHelper.getThemeAlias(resource.getName(),
  258. pathPrefix);
  259. // OsgiVaadinTheme provides folders so we have to add a wildcard
  260. alias = alias + "/*";
  261. path = PathFormatHelper.getThemePath(resource.getName());
  262. // save cast because OsgiVaadinTheme is a super class of T
  263. interfaceType = (Class<? super T>) OsgiVaadinTheme.class;
  264. } else {
  265. alias = PathFormatHelper
  266. .getRootResourceAlias(resource.getName(), pathPrefix);
  267. path = PathFormatHelper.getRootResourcePath(resource.getName());
  268. interfaceType = OsgiVaadinResource.class;
  269. }
  270. // remove the empty prefixed slash
  271. alias = alias.substring(1);
  272. final String contextFilter = "("
  273. + HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_NAME + "="
  274. + vaadinService.getContextName() + ")";
  275. // register a OSGi http resource based on the whiteboard pattern
  276. final Dictionary<String, String> properties = new Hashtable<>();
  277. properties.put(
  278. HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PATTERN,
  279. alias);
  280. properties.put(
  281. HttpWhiteboardConstants.HTTP_WHITEBOARD_RESOURCE_PREFIX,
  282. path);
  283. properties.put(
  284. HttpWhiteboardConstants.HTTP_WHITEBOARD_CONTEXT_SELECT,
  285. contextFilter);
  286. resourceRegistration = bundleContext.registerService(interfaceType,
  287. resource, properties);
  288. }
  289. }
  290. }