View Javadoc

1   package com.atlassian.plugin.osgi.factory;
2   
3   import com.atlassian.plugin.IllegalPluginStateException;
4   import com.atlassian.plugin.module.ContainerAccessor;
5   import com.atlassian.plugin.osgi.container.OsgiContainerException;
6   import com.atlassian.plugin.osgi.spring.DefaultSpringContainerAccessor;
7   import com.atlassian.plugin.osgi.util.BundleClassLoaderAccessor;
8   import com.atlassian.plugin.osgi.util.OsgiHeaderUtil;
9   import com.atlassian.plugin.util.resource.AlternativeDirectoryResourceLoader;
10  import com.google.common.base.Function;
11  import org.osgi.framework.Bundle;
12  import org.osgi.framework.Constants;
13  import org.osgi.service.packageadmin.ExportedPackage;
14  import org.osgi.service.packageadmin.PackageAdmin;
15  import org.osgi.util.tracker.ServiceTracker;
16  import org.slf4j.Logger;
17  import org.slf4j.LoggerFactory;
18  
19  import java.io.InputStream;
20  import java.net.URL;
21  import java.util.Dictionary;
22  import java.util.HashSet;
23  import java.util.Set;
24  
25  import static com.atlassian.fugue.Option.option;
26  import static com.atlassian.fugue.Suppliers.alwaysFalse;
27  import static com.google.common.base.Preconditions.checkNotNull;
28  
29  /**
30   * Helper class that implements the methods assuming the OSGi plugin has been installed
31   *
32   * @since 2.2.0
33   */
34  final class OsgiPluginInstalledHelper implements OsgiPluginHelper
35  {
36      private static final Logger logger = LoggerFactory.getLogger(OsgiPluginInstalledHelper.class);
37      
38      private final ClassLoader bundleClassLoader;
39      private final Bundle bundle;
40      private final PackageAdmin packageAdmin;
41  
42      private volatile ContainerAccessor containerAccessor;
43      private volatile ServiceTracker[] serviceTrackers;
44  
45      /**
46       * @param bundle The bundle
47       * @param packageAdmin The package admin
48       */
49      public OsgiPluginInstalledHelper(final Bundle bundle, final PackageAdmin packageAdmin)
50      {
51          this.bundle = checkNotNull(bundle);
52          this.packageAdmin = checkNotNull(packageAdmin);
53          bundleClassLoader = BundleClassLoaderAccessor.getClassLoader(bundle, new AlternativeDirectoryResourceLoader());
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          logger.debug("Not installing OSGi plugin '{}' since it's already installed.", bundle.getSymbolicName());
84          throw new IllegalPluginStateException("Plugin '" + bundle.getSymbolicName() + "' has already been installed");
85      }
86  
87      public void onEnable(final ServiceTracker... serviceTrackers) throws OsgiContainerException
88      {
89          for (final ServiceTracker svc : checkNotNull(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 Set<String> getRequiredPlugins()
116     {
117         /* A bundle must move from INSTALLED to RESOLVED before we can get its import */
118         if (bundle.getState() == Bundle.INSTALLED)
119         {
120             logger.debug("Bundle is in INSTALLED for {}", bundle.getSymbolicName());
121             packageAdmin.resolveBundles(new Bundle[]{bundle});
122             logger.debug("Bundle state is now {}", bundle.getState());
123         }
124         
125         final Set<String> keys = new HashSet<String>();
126         getRequiredPluginsFromExports(keys);
127 
128         // we can't get required plugins from services, since services could have different cardinalities and you can't
129         // detect that from looking at the service reference.
130         return keys;
131     }
132 
133     private void getRequiredPluginsFromExports(Set<String> keys)
134     {
135         // Get a set of all packages that this plugin imports
136         final Set<String> imports = new HashSet<String>(OsgiHeaderUtil.parseHeader((String) getBundle().getHeaders().get(Constants.IMPORT_PACKAGE)).keySet());
137 
138         // Add in all explicit (non-wildcard) dynamic package imports
139         imports.addAll(OsgiHeaderUtil.parseHeader((String) getBundle().getHeaders().get(Constants.DYNAMICIMPORT_PACKAGE)).keySet());
140 
141         // For each import, determine what bundle provides the package
142         for (final String imp : imports)
143         {
144             // Get a list of package exports for this package
145             final ExportedPackage[] exports = packageAdmin.getExportedPackages(imp);
146             if (exports != null)
147             {
148                 // For each exported package, determine if we are a consumer
149                 for (final ExportedPackage export : exports)
150                 {
151                     // Get a list of bundles that consume that package
152                     final Bundle[] importingBundles = export.getImportingBundles();
153                     if (importingBundles != null)
154                     {
155                         // For each importing bundle, determine if it is us
156                         for (final Bundle importingBundle : importingBundles)
157                         {
158                             // If we are the bundle consumer, or importer, then add the exporter as a required plugin
159                             if (getBundle() == importingBundle)
160                             {
161                                 keys.add(OsgiHeaderUtil.getPluginKey(export.getExportingBundle()));
162                                 break;
163                             }
164                         }
165                     }
166                 }
167             }
168         }
169     }
170 
171     public void setPluginContainer(final Object container)
172     {
173         if (container == null)
174         {
175             containerAccessor = null;
176         }
177         else if (container instanceof ContainerAccessor)
178         {
179             containerAccessor = (ContainerAccessor) container;
180         }
181         else
182         {
183             containerAccessor = new DefaultSpringContainerAccessor(container);
184         }
185     }
186 
187     public ContainerAccessor getContainerAccessor()
188     {
189         return containerAccessor;
190     }
191 
192     /**
193      * @throws IllegalPluginStateException if the plugin container is not initialized
194      */
195     public ContainerAccessor getRequiredContainerAccessor() throws IllegalPluginStateException
196     {
197         if (containerAccessor == null)
198         {
199             throw new IllegalStateException("Cannot create object because the plugin container is unavailable.");
200         }
201         return containerAccessor;
202     }
203 
204     @Override
205     public boolean isRemotePlugin()
206     {
207         return option(getBundle().getHeaders()).fold(alwaysFalse(), new Function<Dictionary, Boolean>()
208         {
209             @Override
210             public Boolean apply(Dictionary headers)
211             {
212                 return headers.get(OsgiPlugin.REMOTE_PLUGIN_KEY) != null;
213             }
214         });
215     }
216 }