View Javadoc
1   /*
2    * #%L
3    * wcm.io
4    * %%
5    * Copyright (C) 2015 wcm.io
6    * %%
7    * Licensed under the Apache License, Version 2.0 (the "License");
8    * you may not use this file except in compliance with the License.
9    * You may obtain a copy of the License at
10   *
11   *      http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * #L%
19   */
20  package io.wcm.caravan.hal.docs.impl;
21  
22  import io.wcm.caravan.jaxrs.publisher.ApplicationPath;
23  
24  import java.util.Dictionary;
25  import java.util.Hashtable;
26  
27  import org.apache.felix.scr.annotations.Activate;
28  import org.apache.felix.scr.annotations.Component;
29  import org.apache.felix.scr.annotations.Deactivate;
30  import org.apache.felix.scr.annotations.Reference;
31  import org.osgi.framework.Bundle;
32  import org.osgi.framework.BundleContext;
33  import org.osgi.framework.BundleEvent;
34  import org.osgi.service.component.ComponentConstants;
35  import org.osgi.service.component.ComponentContext;
36  import org.osgi.service.component.ComponentFactory;
37  import org.osgi.service.component.ComponentInstance;
38  import org.osgi.service.http.HttpService;
39  import org.osgi.service.http.NamespaceException;
40  import org.osgi.util.tracker.BundleTracker;
41  import org.osgi.util.tracker.BundleTrackerCustomizer;
42  import org.slf4j.Logger;
43  import org.slf4j.LoggerFactory;
44  
45  /**
46   * Tracks bundles that contain HAL documentation files generated by hal-docs-maven-plugin, and mounts them
47   * via HTTP service to /docs/api/{serviceId}/*.
48   */
49  @Component(immediate = true)
50  public class HalDocsBundleTracker implements BundleTrackerCustomizer<ComponentInstance> {
51  
52    static final String DOCS_RESOURCES_URI_PREFIX = "/docs/resources";
53    static final String CLASSPATH_FRONTEND = "HALDOCS-TEMPLATE-INF/frontend";
54  
55    private static final Logger log = LoggerFactory.getLogger(HalDocsBundleTracker.class);
56  
57    private BundleContext bundleContext;
58    private BundleTracker bundleTracker;
59  
60    @Reference(target = "(" + ComponentConstants.COMPONENT_FACTORY + "=" + HalDocsServlet.FACTORY + ")")
61    private ComponentFactory servletFactory;
62  
63    @Reference
64    private HttpService httpService;
65  
66    @Activate
67    void activate(ComponentContext componentContext) throws NamespaceException {
68      bundleContext = componentContext.getBundleContext();
69      this.bundleTracker = new BundleTracker<ComponentInstance>(bundleContext, Bundle.ACTIVE, this);
70      this.bundleTracker.open();
71  
72      // mount static resources for docs frontend
73      httpService.registerResources(DOCS_RESOURCES_URI_PREFIX, CLASSPATH_FRONTEND, null);
74    }
75  
76    @Deactivate
77    void deactivate(ComponentContext componentContext) {
78      this.bundleTracker.close();
79      httpService.unregister(DOCS_RESOURCES_URI_PREFIX);
80    }
81  
82    @Override
83    public ComponentInstance addingBundle(Bundle bundle, BundleEvent event) {
84      String docsPath = DocsPath.get(bundle);
85      if (docsPath != null) {
86  
87        if (log.isInfoEnabled()) {
88          log.info("Mount HAL docs for {} to {}", bundle.getSymbolicName(), docsPath);
89        }
90  
91        // register HAL docs servlet on HTTP whiteboard
92        Dictionary<String, Object> serviceConfig = new Hashtable<>();
93        serviceConfig.put("alias", docsPath);
94        serviceConfig.put(HalDocsServlet.PROPERTY_BUNDLE, bundle);
95        return servletFactory.newInstance(serviceConfig);
96      }
97      return null;
98    }
99  
100   @Override
101   public void modifiedBundle(Bundle bundle, BundleEvent event, ComponentInstance componentInstance) {
102     // nothing to do
103   }
104 
105   @Override
106   public void removedBundle(Bundle bundle, BundleEvent event, ComponentInstance componentInstance) {
107     if (componentInstance == null) {
108       return;
109     }
110     if (log.isInfoEnabled()) {
111       String applicationPath = ApplicationPath.get(bundle);
112       String docsPath = DocsPath.get(applicationPath);
113       log.info("Unmount HAL docs for {} from {}", bundle.getSymbolicName(), docsPath);
114     }
115     componentInstance.dispose();
116   }
117 
118 }