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 import java.util.jar.JarFile;
29
30
31
32
33 public class FelixOsgiContainerManager implements OsgiContainerManager
34 {
35 private static final Log log = LogFactory.getLog(FelixOsgiContainerManager.class);
36 private BundleRegistration registration = null;
37 private Felix felix = null;
38 private boolean felixRunning = false;
39 private File cacheDirectory;
40 private boolean disableMultipleBundleVersions = true;
41
42 private final URL frameworkBundlesUrl;
43 private PackageScannerConfiguration packageScannerConfig;
44 public static final String OSGI_FRAMEWORK_BUNDLES_ZIP = "osgi-framework-bundles.zip";
45 private File frameworkBundlesDir;
46
47
48 public FelixOsgiContainerManager(File frameworkBundlesDir, PackageScannerConfiguration packageScannerConfig)
49 {
50 this(ClassLoaderUtils.getResource(OSGI_FRAMEWORK_BUNDLES_ZIP, FelixOsgiContainerManager.class), frameworkBundlesDir, packageScannerConfig);
51 }
52 public FelixOsgiContainerManager(URL frameworkBundlesZip, File frameworkBundlesDir, PackageScannerConfiguration packageScannerConfig)
53 {
54 if (frameworkBundlesZip == null)
55 throw new IllegalArgumentException("The framework bundles zip is required");
56
57 this.frameworkBundlesUrl = frameworkBundlesZip;
58 this.packageScannerConfig = packageScannerConfig;
59 this.frameworkBundlesDir = frameworkBundlesDir;
60 }
61
62 public void setDisableMultipleBundleVersions(boolean val)
63 {
64 this.disableMultipleBundleVersions = val;
65 }
66
67 public void start(HostComponentProvider provider) throws OsgiContainerException
68 {
69
70 initialiseCacheDirectory();
71
72 DefaultComponentRegistrar registrar = collectHostComponents(provider);
73
74 final Map configMap = new StringMap(false);
75
76 configMap.put(FelixConstants.EMBEDDED_EXECUTION_PROP, "true");
77
78
79 configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES, OsgiHeaderUtil.determineExports(registrar.getRegistry(), packageScannerConfig));
80
81
82 configMap.put(BundleCache.CACHE_PROFILE_DIR_PROP, cacheDirectory.getAbsolutePath());
83
84 FelixLoggerBridge bridge = new FelixLoggerBridge(log);
85 configMap.put(FelixConstants.LOG_LEVEL_PROP, String.valueOf(bridge.getLogLevel()));
86
87 try
88 {
89
90 registration = new BundleRegistration(frameworkBundlesUrl, frameworkBundlesDir, registrar);
91 final List<BundleActivator> list = new ArrayList<BundleActivator>();
92 list.add(registration);
93
94
95
96 felix = new Felix(bridge, configMap, list);
97
98
99 Thread t = new Thread() {
100 @Override
101 public void run() {
102 try
103 {
104
105 felix.start();
106 felixRunning = true;
107 } catch (BundleException e)
108 {
109 throw new OsgiContainerException("Unable to start felix", e);
110 }
111 }
112 };
113 t.setDaemon(true);
114 t.start();
115
116
117 t.join(10 * 60 * 1000);
118
119
120
121 }
122 catch (Exception ex)
123 {
124 throw new OsgiContainerException("Unable to start OSGi container", ex);
125 }
126 }
127
128 public void stop() throws OsgiContainerException
129 {
130 try
131 {
132 if (felixRunning)
133 felix.stop();
134
135 felixRunning = false;
136 felix = null;
137 } catch (BundleException e)
138 {
139 throw new OsgiContainerException("Unable to stop OSGi container", e);
140 }
141 }
142
143 public Bundle[] getBundles()
144 {
145 return registration.getBundles();
146 }
147
148 public ServiceReference[] getRegisteredServices()
149 {
150 return felix.getRegisteredServices();
151 }
152
153 public Bundle installBundle(File file) throws OsgiContainerException
154 {
155 try
156 {
157 return registration.install(file, disableMultipleBundleVersions);
158 } catch (BundleException e)
159 {
160 throw new OsgiContainerException("Unable to install bundle", e);
161 }
162 }
163
164 public void reloadHostComponents(HostComponentProvider provider)
165 {
166 registration.reloadHostComponents(collectHostComponents(provider));
167 }
168
169 DefaultComponentRegistrar collectHostComponents(HostComponentProvider provider)
170 {
171 DefaultComponentRegistrar registrar = new DefaultComponentRegistrar();
172 if (provider != null)
173 provider.provide(registrar);
174 return registrar;
175 }
176
177 void initialiseCacheDirectory() throws OsgiContainerException
178 {
179 try
180 {
181 cacheDirectory = new File(File.createTempFile("foo", "bar").getParentFile(), "felix");
182 if (cacheDirectory.exists())
183 FileUtils.deleteDirectory(cacheDirectory);
184
185 cacheDirectory.mkdir();
186 cacheDirectory.deleteOnExit();
187 } catch (IOException e)
188 {
189 throw new OsgiContainerException("Cannot create cache directory", e);
190 }
191 }
192
193 public boolean isRunning()
194 {
195 return felixRunning;
196 }
197
198 public List<HostComponentRegistration> getHostComponentRegistrations()
199 {
200 return registration.hostComponentRegistrations;
201 }
202
203
204
205
206
207 static class BundleRegistration implements BundleActivator, BundleListener
208 {
209 private BundleContext bundleContext;
210 private DefaultComponentRegistrar registrar;
211 private List<ServiceRegistration> hostServicesReferences;
212 private List<HostComponentRegistration> hostComponentRegistrations;
213 private URL frameworkBundlesUrl;
214 private File frameworkBundlesDir;
215
216 public BundleRegistration(URL frameworkBundlesUrl, File frameworkBundlesDir, DefaultComponentRegistrar registrar)
217 {
218 this.registrar = registrar;
219 this.frameworkBundlesUrl = frameworkBundlesUrl;
220 this.frameworkBundlesDir = frameworkBundlesDir;
221 }
222
223 public void start(BundleContext context) throws Exception {
224 this.bundleContext = context;
225 context.addBundleListener(this);
226
227 reloadHostComponents(registrar);
228 extractAndInstallFrameworkBundles();
229 }
230
231 public void reloadHostComponents(DefaultComponentRegistrar registrar)
232 {
233
234 if (hostServicesReferences != null) {
235 for (ServiceRegistration reg : hostServicesReferences)
236 reg.unregister();
237 }
238
239
240 hostServicesReferences = registrar.writeRegistry(bundleContext);
241 hostComponentRegistrations = registrar.getRegistry();
242 }
243
244 public void stop(BundleContext ctx) throws Exception {
245 }
246
247 public void bundleChanged(BundleEvent evt) {
248 switch (evt.getType()) {
249 case BundleEvent.INSTALLED:
250 log.warn("Installed bundle " + evt.getBundle().getSymbolicName() + " ("+evt.getBundle().getBundleId()+")");
251 break;
252 case BundleEvent.STARTED:
253 log.warn("Started bundle " + evt.getBundle().getSymbolicName() + " ("+evt.getBundle().getBundleId()+")");
254 break;
255 case BundleEvent.STOPPED:
256 log.warn("Stopped bundle " + evt.getBundle().getSymbolicName() + " ("+evt.getBundle().getBundleId()+")");
257 break;
258 case BundleEvent.UNINSTALLED:
259 log.warn("Uninstalled bundle " + evt.getBundle().getSymbolicName() + " ("+evt.getBundle().getBundleId()+")");
260 break;
261 }
262 }
263
264 public Bundle install(File path, boolean uninstallOtherVersions) throws BundleException
265 {
266 Bundle bundle;
267
268 if (uninstallOtherVersions)
269 {
270 try
271 {
272 JarFile jar = new JarFile(path);
273 String name = jar.getManifest().getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
274 for (Bundle oldBundle : bundleContext.getBundles())
275 {
276 if (name.equals(oldBundle.getSymbolicName()))
277 {
278 log.info("Uninstalling existing version "+oldBundle.getHeaders().get(Constants.BUNDLE_VERSION));
279 oldBundle.uninstall();
280 }
281 }
282
283 } catch (IOException e)
284 {
285 throw new BundleException("Invalid bundle format", e);
286 }
287 }
288 try
289 {
290 bundle = bundleContext.installBundle(path.toURL().toString());
291 } catch (MalformedURLException e)
292 {
293 throw new BundleException("Invalid path: "+path);
294 }
295 return bundle;
296 }
297
298 public Bundle[] getBundles()
299 {
300 return bundleContext.getBundles();
301 }
302
303 public List<HostComponentRegistration> getHostComponentRegistrations()
304 {
305 return hostComponentRegistrations;
306 }
307
308 private void extractAndInstallFrameworkBundles() throws IOException, BundleException
309 {
310 List<Bundle> bundles = new ArrayList<Bundle>();
311 com.atlassian.plugin.util.FileUtils.conditionallyExtractZipFile(frameworkBundlesUrl, frameworkBundlesDir);
312 for (File bundleFile : frameworkBundlesDir.listFiles(new FilenameFilter() {
313 public boolean accept(File file, String s) {
314 return s.endsWith(".jar");
315 }
316 }))
317 {
318 bundles.add(install(bundleFile, false));
319 }
320
321 for (Bundle bundle : bundles)
322 {
323 bundle.start();
324 }
325 }
326 }
327
328 }