View Javadoc

1   package com.atlassian.plugin.osgi.factory;
2   
3   import java.io.InputStream;
4   import java.net.URL;
5   import java.util.HashSet;
6   import java.util.Set;
7   
8   import com.atlassian.plugin.AutowireCapablePlugin;
9   import com.atlassian.plugin.IllegalPluginStateException;
10  import com.atlassian.plugin.module.ContainerAccessor;
11  import com.atlassian.plugin.osgi.container.OsgiContainerException;
12  import com.atlassian.plugin.osgi.spring.DefaultSpringContainerAccessor;
13  import com.atlassian.plugin.osgi.util.BundleClassLoaderAccessor;
14  import com.atlassian.plugin.osgi.util.OsgiHeaderUtil;
15  import com.atlassian.plugin.util.resource.AlternativeDirectoryResourceLoader;
16  
17  import org.apache.commons.lang.Validate;
18  import org.osgi.framework.Bundle;
19  import org.osgi.framework.Constants;
20  import org.osgi.service.packageadmin.ExportedPackage;
21  import org.osgi.service.packageadmin.PackageAdmin;
22  import org.osgi.util.tracker.ServiceTracker;
23  import org.slf4j.Logger;
24  import org.slf4j.LoggerFactory;
25  
26  /**
27   * Helper class that implements the methods assuming the OSGi plugin has been installed
28   *
29   * @since 2.2.0
30   */
31  class OsgiPluginInstalledHelper implements OsgiPluginHelper
32  {
33      private static final Logger log = LoggerFactory.getLogger(OsgiPluginInstalledHelper.class);
34      
35      private final ClassLoader bundleClassLoader;
36      private final Bundle bundle;
37      private final PackageAdmin packageAdmin;
38  
39      private volatile ContainerAccessor containerAccessor;
40      private volatile ServiceTracker[] serviceTrackers;
41  
42      /**
43       * @param bundle The bundle
44       * @param packageAdmin The package admin
45       */
46      public OsgiPluginInstalledHelper(final Bundle bundle, final PackageAdmin packageAdmin)
47      {
48          Validate.notNull(bundle);
49          Validate.notNull(packageAdmin);
50          this.bundle = bundle;
51          bundleClassLoader = BundleClassLoaderAccessor.getClassLoader(bundle, new AlternativeDirectoryResourceLoader());
52          this.packageAdmin = packageAdmin;
53      }
54  
55      public Bundle getBundle()
56      {
57          return bundle;
58      }
59  
60      public <T> Class<T> loadClass(final String clazz, final Class<?> callingClass) throws ClassNotFoundException
61      {
62          return BundleClassLoaderAccessor.loadClass(getBundle(), clazz);
63      }
64  
65      public URL getResource(final String name)
66      {
67          return bundleClassLoader.getResource(name);
68      }
69  
70      public InputStream getResourceAsStream(final String name)
71      {
72          return bundleClassLoader.getResourceAsStream(name);
73      }
74  
75      public ClassLoader getClassLoader()
76      {
77          return bundleClassLoader;
78      }
79  
80      public Bundle install()
81      {
82          throw new IllegalPluginStateException("Plugin '" + bundle.getSymbolicName() + "' has already been installed");
83      }
84  
85      public void onEnable(final ServiceTracker... serviceTrackers) throws OsgiContainerException
86      {
87          Validate.notNull(serviceTrackers);
88          
89          for (final ServiceTracker svc : serviceTrackers)
90          {
91              svc.open();
92          }
93          
94          this.serviceTrackers = serviceTrackers;
95      }
96  
97      public void onDisable() throws OsgiContainerException
98      {
99          final ServiceTracker[] serviceTrackers = this.serviceTrackers; // cache a copy locally for multi-threaded goodness
100         if(serviceTrackers != null)
101         {
102             for (final ServiceTracker svc : serviceTrackers)
103             {
104                 svc.close();
105             }
106             this.serviceTrackers = null;
107         }
108         setPluginContainer(null);
109     }
110 
111     public void onUninstall() throws OsgiContainerException
112     {
113     }
114 
115     public <T> T autowire(final Class<T> clazz, final AutowireCapablePlugin.AutowireStrategy autowireStrategy) throws IllegalPluginStateException
116     {
117         assertPluginContainerIsAvailable();
118         return containerAccessor.createBean(clazz);
119     }
120 
121     /**
122      * If a plugin container is required, it looks for the plugin container and calls autowire().  If not, the object
123      * is untouched.
124      *
125      * @param instance The instance to autowire
126      * @param autowireStrategy The autowire strategy to use The strategy to use, only respected if a plugin container
127      *                         is available
128      * @return The autowired instance
129      * @throws IllegalPluginStateException If a plugin container is required but not available
130      */
131     public void autowire(final Object instance, final AutowireCapablePlugin.AutowireStrategy autowireStrategy) throws IllegalPluginStateException
132     {
133         assertPluginContainerIsAvailable();
134         containerAccessor.injectBean(instance);
135     }
136 
137     public Set<String> getRequiredPlugins()
138     {
139         /* A bundle must move from INSTALLED to RESOLVED before we can get its import */
140         if (bundle.getState() == Bundle.INSTALLED)
141         {
142             log.debug("Bundle is in INSTALLED for {}", bundle.getSymbolicName());
143             packageAdmin.resolveBundles(new Bundle[]{bundle});
144             log.debug("Bundle state is now {}", bundle.getState());
145         }
146         
147         final Set<String> keys = new HashSet<String>();
148         getRequiredPluginsFromExports(keys);
149 
150         // we can't get required plugins from services, since services could have different cardinalities and you can't
151         // detect that from looking at the service reference.
152         return keys;
153     }
154 
155     private void getRequiredPluginsFromExports(Set<String> keys)
156     {
157         // Get a set of all packages that this plugin imports
158         final Set<String> imports = new HashSet<String>(OsgiHeaderUtil.parseHeader((String) getBundle().getHeaders().get(Constants.IMPORT_PACKAGE)).keySet());
159 
160         // Add in all explicit (non-wildcard) dynamic package imports
161         imports.addAll(OsgiHeaderUtil.parseHeader((String) getBundle().getHeaders().get(Constants.DYNAMICIMPORT_PACKAGE)).keySet());
162 
163         // For each import, determine what bundle provides the package
164         for (final String imp : imports)
165         {
166             // Get a list of package exports for this package
167             final ExportedPackage[] exports = packageAdmin.getExportedPackages(imp);
168             if (exports != null)
169             {
170                 // For each exported package, determine if we are a consumer
171                 for (final ExportedPackage export : exports)
172                 {
173                     // Get a list of bundles that consume that package
174                     final Bundle[] importingBundles = export.getImportingBundles();
175                     if (importingBundles != null)
176                     {
177                         // For each importing bundle, determine if it is us
178                         for (final Bundle importingBundle : importingBundles)
179                         {
180                             // If we are the bundle consumer, or importer, then add the exporter as a required plugin
181                             if (getBundle() == importingBundle)
182                             {
183                                 keys.add(OsgiHeaderUtil.getPluginKey(export.getExportingBundle()));
184                                 break;
185                             }
186                         }
187                     }
188                 }
189             }
190         }
191     }
192 
193     public void setPluginContainer(final Object container)
194     {
195         if (container == null)
196         {
197             containerAccessor = null;
198         }
199         else if (container instanceof ContainerAccessor)
200         {
201             containerAccessor = (ContainerAccessor) container;
202         }
203         else
204         {
205             containerAccessor = new DefaultSpringContainerAccessor(container);
206         }
207     }
208 
209     public ContainerAccessor getContainerAccessor()
210     {
211         return containerAccessor;
212     }
213 
214     /**
215      * @throws IllegalPluginStateException if the plugin container is not initialized
216      */
217     private void assertPluginContainerIsAvailable() throws IllegalPluginStateException
218     {
219         if (containerAccessor == null)
220         {
221             throw new IllegalStateException("Cannot autowire object because the plugin container is unavailable.");
222         }
223     }
224 
225 }