View Javadoc

1   package com.atlassian.plugin.osgi.container.felix;
2   
3   import com.atlassian.plugin.osgi.container.OsgiContainerException;
4   import com.atlassian.plugin.osgi.container.OsgiContainerManager;
5   import com.atlassian.plugin.osgi.container.PackageScannerConfiguration;
6   import com.atlassian.plugin.osgi.hostcomponents.HostComponentProvider;
7   import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
8   import com.atlassian.plugin.osgi.hostcomponents.impl.DefaultComponentRegistrar;
9   import com.atlassian.plugin.osgi.util.OsgiHeaderUtil;
10  import com.atlassian.plugin.util.ClassLoaderUtils;
11  import org.apache.commons.logging.Log;
12  import org.apache.commons.logging.LogFactory;
13  import org.apache.commons.io.FileUtils;
14  import org.apache.felix.framework.Felix;
15  import org.apache.felix.framework.cache.BundleCache;
16  import org.apache.felix.framework.util.FelixConstants;
17  import org.apache.felix.framework.util.StringMap;
18  import org.osgi.framework.*;
19  import static org.twdata.pkgscanner.PackageScanner.jars;
20  import static org.twdata.pkgscanner.PackageScanner.packages;
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.FilenameFilter;
25  import java.net.MalformedURLException;
26  import java.net.URL;
27  import java.util.*;
28  
29  /**
30   * Felix implementation of the OSGi container manager
31   */
32  public class FelixOsgiContainerManager implements OsgiContainerManager
33  {
34      private static final Log log = LogFactory.getLog(FelixOsgiContainerManager.class);
35      private BundleRegistration registration = null;
36      private Felix felix = null;
37      private boolean felixRunning = false;
38      private File cacheDirectory;
39  
40      private final URL frameworkBundlesUrl;
41      private PackageScannerConfiguration packageScannerConfig;
42      public static final String OSGI_FRAMEWORK_BUNDLES_ZIP = "osgi-framework-bundles.zip";
43      private File frameworkBundlesDir;
44  
45  
46      public FelixOsgiContainerManager(File frameworkBundlesDir, PackageScannerConfiguration packageScannerConfig)
47      {
48          this(ClassLoaderUtils.getResource(OSGI_FRAMEWORK_BUNDLES_ZIP, FelixOsgiContainerManager.class), frameworkBundlesDir, packageScannerConfig);
49      }
50      public FelixOsgiContainerManager(URL frameworkBundlesZip, File frameworkBundlesDir, PackageScannerConfiguration packageScannerConfig)
51      {
52          if (frameworkBundlesZip == null)
53              throw new IllegalArgumentException("The framework bundles zip is required");
54  
55          this.frameworkBundlesUrl = frameworkBundlesZip;
56          this.packageScannerConfig = packageScannerConfig;
57          this.frameworkBundlesDir = frameworkBundlesDir;
58      }
59  
60  
61  
62      public void start(HostComponentProvider provider) throws OsgiContainerException
63      {
64  
65          initialiseCacheDirectory();
66  
67          DefaultComponentRegistrar registrar = collectHostComponents(provider);
68          // Create a case-insensitive configuration property map.
69          final Map configMap = new StringMap(false);
70          // Configure the Felix instance to be embedded.
71          configMap.put(FelixConstants.EMBEDDED_EXECUTION_PROP, "true");
72          // Add the bundle provided service interface package and the core OSGi
73          // packages to be exported from the class path via the system bundle.
74          configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES, OsgiHeaderUtil.determineExports(registrar.getRegistry(), packageScannerConfig));
75  
76          // Explicitly specify the directory to use for caching bundles.
77          configMap.put(BundleCache.CACHE_PROFILE_DIR_PROP, cacheDirectory.getAbsolutePath());
78  
79          FelixLoggerBridge bridge = new FelixLoggerBridge(log);
80          configMap.put(FelixConstants.LOG_LEVEL_PROP, String.valueOf(bridge.getLogLevel()));
81  
82          try
83          {
84              // Create host activator;
85              registration = new BundleRegistration(frameworkBundlesUrl, frameworkBundlesDir, registrar);
86              final List<BundleActivator> list = new ArrayList<BundleActivator>();
87              list.add(registration);
88  
89              // Now create an instance of the framework with
90              // our configuration properties and activator.
91              felix = new Felix(bridge, configMap, list);
92  
93              // Now start Felix instance.  Starting in a different thread to explicity set daemon status
94              Thread t = new Thread() {
95                  @Override
96                  public void run() {
97                      try
98                      {
99  
100                         felix.start();
101                         felixRunning = true;
102                     } catch (BundleException e)
103                     {
104                         throw new OsgiContainerException("Unable to start felix", e);
105                     }
106                 }
107             };
108             t.setDaemon(true);
109             t.start();
110 
111             // Give it 10 seconds
112             t.join(10 * 60 * 1000);
113 
114 
115 
116         }
117         catch (Exception ex)
118         {
119             throw new OsgiContainerException("Unable to start OSGi container", ex);
120         }
121     }
122 
123     public void stop() throws OsgiContainerException
124     {
125         try
126         {
127             felix.stop();
128             felixRunning = false;
129             felix = null;
130         } catch (BundleException e)
131         {
132             throw new OsgiContainerException("Unable to stop OSGi container", e);
133         }
134     }
135 
136     public Bundle[] getBundles()
137     {
138         return registration.getBundles();
139     }
140 
141     public ServiceReference[] getRegisteredServices()
142     {
143         return felix.getRegisteredServices();
144     }
145 
146     public Bundle installBundle(File file) throws OsgiContainerException
147     {
148         try
149         {
150             return registration.install(file);
151         } catch (BundleException e)
152         {
153             throw new OsgiContainerException("Unable to install bundle", e);
154         }
155     }
156 
157     public void reloadHostComponents(HostComponentProvider provider)
158     {
159         registration.reloadHostComponents(collectHostComponents(provider));
160     }
161 
162     DefaultComponentRegistrar collectHostComponents(HostComponentProvider provider)
163     {
164         DefaultComponentRegistrar registrar = new DefaultComponentRegistrar();
165         if (provider != null)
166             provider.provide(registrar);
167         return registrar;
168     }
169 
170     void initialiseCacheDirectory() throws OsgiContainerException
171     {
172         try
173         {
174             cacheDirectory = new File(File.createTempFile("foo", "bar").getParentFile(), "felix");
175             if (cacheDirectory.exists())
176                 FileUtils.deleteDirectory(cacheDirectory);
177 
178             cacheDirectory.mkdir();
179             cacheDirectory.deleteOnExit();
180         } catch (IOException e)
181         {
182             throw new OsgiContainerException("Cannot create cache directory", e);
183         }
184     }
185 
186     public boolean isRunning()
187     {
188         return felixRunning;
189     }
190 
191     public List<HostComponentRegistration> getHostComponentRegistrations()
192     {
193         return registration.hostComponentRegistrations;
194     }
195 
196     /**
197      * Manages framwork-level framework bundles and host components registration, and individual plugin bundle
198      * installation and removal.
199      */
200     static class BundleRegistration implements BundleActivator, BundleListener
201     {
202         private BundleContext bundleContext;
203         private DefaultComponentRegistrar registrar;
204         private List<ServiceRegistration> hostServicesReferences;
205         private List<HostComponentRegistration> hostComponentRegistrations;
206         private URL frameworkBundlesUrl;
207         private File frameworkBundlesDir;
208 
209         public BundleRegistration(URL frameworkBundlesUrl, File frameworkBundlesDir, DefaultComponentRegistrar registrar)
210         {
211             this.registrar = registrar;
212             this.frameworkBundlesUrl = frameworkBundlesUrl;
213             this.frameworkBundlesDir = frameworkBundlesDir;
214         }
215 
216         public void start(BundleContext context) throws Exception {
217             this.bundleContext = context;
218             context.addBundleListener(this);
219 
220             reloadHostComponents(registrar);
221             extractAndInstallFrameworkBundles();
222         }
223 
224         public void reloadHostComponents(DefaultComponentRegistrar registrar)
225         {
226             // Unregister any existing host components
227             if (hostServicesReferences != null) {
228                 for (ServiceRegistration reg : hostServicesReferences)
229                     reg.unregister();
230             }
231             
232             // Register host components as OSGi services
233             hostServicesReferences = registrar.writeRegistry(bundleContext);
234             hostComponentRegistrations = registrar.getRegistry();
235         }
236 
237         public void stop(BundleContext ctx) throws Exception {
238         }
239 
240         public void bundleChanged(BundleEvent evt) {
241             switch (evt.getType()) {
242                 case BundleEvent.INSTALLED:
243                     log.warn("Installed bundle " + evt.getBundle().getSymbolicName() + " ("+evt.getBundle().getBundleId()+")");
244                     break;
245                 case BundleEvent.STARTED:
246                     log.warn("Started bundle " + evt.getBundle().getSymbolicName() + " ("+evt.getBundle().getBundleId()+")");
247                     break;
248                 case BundleEvent.STOPPED:
249                     log.warn("Stopped bundle " + evt.getBundle().getSymbolicName() + " ("+evt.getBundle().getBundleId()+")");
250                     break;
251                 case BundleEvent.UNINSTALLED:
252                     log.warn("Uninstalled bundle " + evt.getBundle().getSymbolicName() + " ("+evt.getBundle().getBundleId()+")");
253                     break;
254             }
255         }
256 
257         public Bundle install(File path) throws BundleException
258         {
259             Bundle bundle;
260             try
261             {
262 
263                 bundle = bundleContext.installBundle(path.toURL().toString());
264             } catch (MalformedURLException e)
265             {
266                 throw new BundleException("Invalid path: "+path);
267             }
268             return bundle;
269         }
270 
271         public Bundle[] getBundles()
272         {
273             return bundleContext.getBundles();
274         }
275 
276         public List<HostComponentRegistration> getHostComponentRegistrations()
277         {
278             return hostComponentRegistrations;
279         }
280 
281         private void extractAndInstallFrameworkBundles() throws IOException, BundleException
282         {
283             List<Bundle> bundles = new ArrayList<Bundle>();
284             com.atlassian.plugin.util.FileUtils.conditionallyExtractZipFile(frameworkBundlesUrl, frameworkBundlesDir);
285             for (File bundleFile : frameworkBundlesDir.listFiles(new FilenameFilter() {
286                     public boolean accept(File file, String s) {
287                         return s.endsWith(".jar"); 
288                     }
289                 }))
290             {
291                 bundles.add(install(bundleFile));
292             }
293 
294             for (Bundle bundle : bundles)
295             {
296                 bundle.start();
297             }
298         }
299     }
300 
301 }