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
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
69 final Map configMap = new StringMap(false);
70
71 configMap.put(FelixConstants.EMBEDDED_EXECUTION_PROP, "true");
72
73
74 configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES, OsgiHeaderUtil.determineExports(registrar.getRegistry(), packageScannerConfig));
75
76
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
85 registration = new BundleRegistration(frameworkBundlesUrl, frameworkBundlesDir, registrar);
86 final List<BundleActivator> list = new ArrayList<BundleActivator>();
87 list.add(registration);
88
89
90
91 felix = new Felix(bridge, configMap, list);
92
93
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
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
198
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
227 if (hostServicesReferences != null) {
228 for (ServiceRegistration reg : hostServicesReferences)
229 reg.unregister();
230 }
231
232
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 }