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.spring.SpringContainerAccessor;
14  import com.atlassian.plugin.osgi.util.BundleClassLoaderAccessor;
15  import com.atlassian.plugin.osgi.util.OsgiHeaderUtil;
16  import com.atlassian.plugin.util.resource.AlternativeDirectoryResourceLoader;
17  
18  import org.apache.commons.lang.Validate;
19  import org.osgi.framework.Bundle;
20  import org.osgi.framework.Constants;
21  import org.osgi.service.packageadmin.ExportedPackage;
22  import org.osgi.service.packageadmin.PackageAdmin;
23  import org.osgi.util.tracker.ServiceTracker;
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  
27  /**
28   * Helper class that implements the methods assuming the OSGi plugin has been installed
29   *
30   * @since 2.2.0
31   */
32  class OsgiPluginInstalledHelper implements OsgiPluginHelper
33  {
34      private static final Logger log = LoggerFactory.getLogger(OsgiPluginInstalledHelper.class);
35      
36      private final ClassLoader bundleClassLoader;
37      private final Bundle bundle;
38      private final PackageAdmin packageAdmin;
39  
40      private volatile SpringContainerAccessor containerAccessor;
41      private volatile ServiceTracker[] serviceTrackers;
42  
43      /**
44       * @param bundle The bundle
45       * @param packageAdmin The package admin
46       */
47      public OsgiPluginInstalledHelper(final Bundle bundle, final PackageAdmin packageAdmin)
48      {
49          Validate.notNull(bundle);
50          Validate.notNull(packageAdmin);
51          this.bundle = bundle;
52          bundleClassLoader = BundleClassLoaderAccessor.getClassLoader(bundle, new AlternativeDirectoryResourceLoader());
53          this.packageAdmin = packageAdmin;
54      }
55  
56      public Bundle getBundle()
57      {
58          return bundle;
59      }
60  
61      public <T> Class<T> loadClass(final String clazz, final Class<?> callingClass) throws ClassNotFoundException
62      {
63          return BundleClassLoaderAccessor.loadClass(getBundle(), clazz);
64      }
65  
66      public URL getResource(final String name)
67      {
68          return bundleClassLoader.getResource(name);
69      }
70  
71      public InputStream getResourceAsStream(final String name)
72      {
73          return bundleClassLoader.getResourceAsStream(name);
74      }
75  
76      public ClassLoader getClassLoader()
77      {
78          return bundleClassLoader;
79      }
80  
81      public Bundle install()
82      {
83          throw new IllegalPluginStateException("Plugin '" + bundle.getSymbolicName() + "' has already been installed");
84      }
85  
86      public void onEnable(final ServiceTracker... serviceTrackers) throws OsgiContainerException
87      {
88          Validate.notNull(serviceTrackers);
89          
90          for (final ServiceTracker svc : serviceTrackers)
91          {
92              svc.open();
93          }
94          
95          this.serviceTrackers = serviceTrackers;
96      }
97  
98      public void onDisable() throws OsgiContainerException
99      {
100         final ServiceTracker[] serviceTrackers = this.serviceTrackers; // cache a copy locally for multi-threaded goodness
101         if(serviceTrackers != null)
102         {
103             for (final ServiceTracker svc : serviceTrackers)
104             {
105                 svc.close();
106             }
107             this.serviceTrackers = null;
108         }
109         setPluginContainer(null);
110     }
111 
112     public void onUninstall() throws OsgiContainerException
113     {
114     }
115 
116     public <T> T autowire(final Class<T> clazz, final AutowireCapablePlugin.AutowireStrategy autowireStrategy) throws IllegalPluginStateException
117     {
118         assertSpringContextAvailable();
119         return containerAccessor.createBean(clazz);
120     }
121 
122     /**
123      * If spring is required, it looks for the spring application context and calls autowire().  If not, the object
124      * is untouched.
125      *
126      * @param instance The instance to autowire
127      * @param autowireStrategy The autowire strategy to use The strategy to use, only respected if spring is available
128      * @return The autowired instance
129      * @throws IllegalPluginStateException If spring is required but not available
130      */
131     public void autowire(final Object instance, final AutowireCapablePlugin.AutowireStrategy autowireStrategy) throws IllegalPluginStateException
132     {
133         assertSpringContextAvailable();
134         containerAccessor.autowireBean(instance, autowireStrategy);
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
200         {
201             containerAccessor = new DefaultSpringContainerAccessor(container);
202         }
203     }
204 
205     public ContainerAccessor getContainerAccessor()
206     {
207         return containerAccessor;
208     }
209 
210     /**
211      * @throws IllegalPluginStateException if the spring context is not initialized
212      */
213     private void assertSpringContextAvailable() throws IllegalPluginStateException
214     {
215         if (containerAccessor == null)
216         {
217             throw new IllegalStateException("Cannot autowire object because the Spring context is unavailable.  " +
218                 "Ensure your OSGi bundle contains the 'Spring-Context' header.");
219         }
220     }
221 
222 }