1 package com.atlassian.plugin.osgi.factory;
2
3 import com.atlassian.plugin.IllegalPluginStateException;
4 import com.atlassian.plugin.PluginArtifact;
5 import com.atlassian.plugin.PluginArtifactBackedPlugin;
6 import com.atlassian.plugin.PluginException;
7 import com.atlassian.plugin.PluginInformation;
8 import com.atlassian.plugin.PluginPermission;
9 import com.atlassian.plugin.PluginState;
10 import com.atlassian.plugin.impl.AbstractPlugin;
11 import com.atlassian.plugin.osgi.container.OsgiContainerException;
12 import com.atlassian.plugin.osgi.container.OsgiContainerManager;
13 import com.atlassian.plugin.osgi.util.BundleClassLoaderAccessor;
14 import com.atlassian.plugin.util.resource.AlternativeDirectoryResourceLoader;
15 import com.google.common.collect.ImmutableSet;
16 import org.osgi.framework.Bundle;
17 import org.osgi.framework.BundleEvent;
18 import org.osgi.framework.BundleException;
19 import org.osgi.framework.Constants;
20 import org.osgi.framework.SynchronousBundleListener;
21 import org.slf4j.Logger;
22 import org.slf4j.LoggerFactory;
23
24 import java.io.File;
25 import java.io.InputStream;
26 import java.net.URL;
27 import java.util.Date;
28 import java.util.jar.Manifest;
29
30 import static com.atlassian.plugin.osgi.util.OsgiHeaderUtil.extractOsgiPluginInformation;
31 import static com.atlassian.plugin.osgi.util.OsgiHeaderUtil.getAttributeWithoutValidation;
32 import static com.atlassian.plugin.osgi.util.OsgiHeaderUtil.getManifest;
33 import static com.google.common.base.Preconditions.checkNotNull;
34
35
36
37
38 public final class OsgiBundlePlugin extends AbstractPlugin implements PluginArtifactBackedPlugin
39 {
40 private static final Logger log = LoggerFactory.getLogger(OsgiBundlePlugin.class);
41
42 private final PluginArtifact pluginArtifact;
43 private final SynchronousBundleListener bundleStartStopListener;
44 private final Date dateLoaded;
45
46
47 private OsgiContainerManager osgiContainerManager;
48
49 private Bundle bundle;
50
51 private ClassLoader bundleClassLoader;
52
53 private OsgiBundlePlugin(final String pluginKey, final PluginArtifact pluginArtifact)
54 {
55 this.pluginArtifact = checkNotNull(pluginArtifact);
56 this.bundleStartStopListener = new SynchronousBundleListener()
57 {
58 public void bundleChanged(final BundleEvent bundleEvent)
59 {
60
61
62 if (bundleEvent.getBundle() == bundle)
63 {
64 if (bundleEvent.getType() == BundleEvent.STOPPING)
65 {
66 setPluginState(PluginState.DISABLED);
67 }
68 else if (bundleEvent.getType() == BundleEvent.STARTED)
69 {
70 setPluginState(PluginState.ENABLED);
71 }
72 }
73 }
74 };
75 this.dateLoaded = new Date();
76 setPluginsVersion(2);
77 setKey(pluginKey);
78 setSystemPlugin(false);
79 }
80
81
82
83
84
85
86
87
88 @Deprecated
89 public OsgiBundlePlugin(final Bundle bundle, final String pluginKey, final PluginArtifact pluginArtifact)
90 {
91 this(pluginKey, pluginArtifact);
92
93 this.bundle = checkNotNull(bundle);
94 this.bundleClassLoader = BundleClassLoaderAccessor.getClassLoader(bundle, new AlternativeDirectoryResourceLoader());
95
96
97
98
99 final PluginInformation pluginInformation = new PluginInformation();
100 pluginInformation.setDescription((String) bundle.getHeaders().get(Constants.BUNDLE_DESCRIPTION));
101 pluginInformation.setVersion((String) bundle.getHeaders().get(Constants.BUNDLE_VERSION));
102 pluginInformation.setVendorName((String) bundle.getHeaders().get(Constants.BUNDLE_VENDOR));
103 pluginInformation.setPermissions(ImmutableSet.of(PluginPermission.EXECUTE_JAVA));
104 setPluginInformation(pluginInformation);
105
106 setName((String) bundle.getHeaders().get(Constants.BUNDLE_NAME));
107 setSystemPlugin(false);
108 }
109
110
111
112
113
114
115
116
117 public OsgiBundlePlugin(final OsgiContainerManager osgiContainerManager,
118 final String pluginKey,
119 final PluginArtifact pluginArtifact)
120 {
121 this(pluginKey, pluginArtifact);
122 this.osgiContainerManager = checkNotNull(osgiContainerManager);
123
124
125 final Manifest manifest = getManifest(pluginArtifact);
126 if (null != manifest)
127 {
128 setName(getAttributeWithoutValidation(manifest, Constants.BUNDLE_NAME));
129
130 setPluginInformation(extractOsgiPluginInformation(manifest, false));
131 }
132
133 }
134
135 @Override
136 public Date getDateLoaded()
137 {
138 return dateLoaded;
139 }
140
141 @Override
142 public Date getDateInstalled()
143 {
144 long date = getPluginArtifact().toFile().lastModified();
145 if (date == 0)
146 {
147 date = getDateLoaded().getTime();
148 }
149 return new Date(date);
150 }
151
152 public boolean isUninstallable()
153 {
154 return true;
155 }
156
157 public boolean isDeleteable()
158 {
159 return true;
160 }
161
162 public boolean isDynamicallyLoaded()
163 {
164 return true;
165 }
166
167 public <T> Class<T> loadClass(final String clazz, final Class<?> callingClass) throws ClassNotFoundException
168 {
169 return BundleClassLoaderAccessor.loadClass(getBundleOrFail(), clazz);
170 }
171
172 public URL getResource(final String name)
173 {
174 return getBundleClassLoaderOrFail().getResource(name);
175 }
176
177 public InputStream getResourceAsStream(final String name)
178 {
179 return getBundleClassLoaderOrFail().getResourceAsStream(name);
180 }
181
182 @Override
183 protected void installInternal() throws OsgiContainerException, IllegalPluginStateException
184 {
185 super.installInternal();
186 if (null != osgiContainerManager)
187 {
188
189 final File file = pluginArtifact.toFile();
190 final boolean allowReference = PluginArtifact.AllowsReference.Default.allowsReference(pluginArtifact);
191 bundle = OsgiContainerManager.AllowsReferenceInstall.Default.installBundle(osgiContainerManager, file, allowReference);
192 bundleClassLoader = BundleClassLoaderAccessor.getClassLoader(bundle, new AlternativeDirectoryResourceLoader());
193 osgiContainerManager = null;
194 }
195 else if (null == bundle)
196 {
197 throw new IllegalPluginStateException("Cannot reuse instance for bundle '" + getKey() + "'");
198 }
199
200 }
201
202 @Override
203 protected void uninstallInternal()
204 {
205 try
206 {
207 if (bundleIsUsable("uninstall"))
208 {
209 if (bundle.getState() != Bundle.UNINSTALLED)
210 {
211 bundle.uninstall();
212 }
213 else
214 {
215
216 log.warn("Bundle '{}' already UNINSTALLED, but still held", getKey());
217 }
218 bundle = null;
219 bundleClassLoader = null;
220 }
221 }
222 catch (final BundleException e)
223 {
224 throw new PluginException(e);
225 }
226 }
227
228 @Override
229 protected PluginState enableInternal()
230 {
231 log.debug("Enabling OSGi bundled plugin '{}'", getKey());
232 try
233 {
234 if (bundleIsUsable("enable"))
235 {
236 if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) == null)
237 {
238 log.debug("Plugin '{}' bundle is NOT a fragment, starting.", getKey());
239 bundle.start();
240 bundle.getBundleContext().addBundleListener(bundleStartStopListener);
241 }
242 else
243 {
244 log.debug("Plugin '{}' bundle is a fragment, not doing anything.", getKey());
245 }
246 }
247
248 return PluginState.ENABLED;
249 }
250 catch (final BundleException e)
251 {
252 throw new PluginException(e);
253 }
254 }
255
256 @Override
257 protected void disableInternal()
258 {
259 try
260 {
261 if (bundleIsUsable("disable"))
262 {
263 if (bundle.getState() == Bundle.ACTIVE)
264 {
265 bundle.stop();
266 }
267 else
268 {
269 log.warn("Cannot disable Bundle '{}', not ACTIVE", getKey());
270 }
271 }
272 }
273 catch (final BundleException e)
274 {
275 throw new PluginException(e);
276 }
277 }
278
279 public ClassLoader getClassLoader()
280 {
281 return getBundleClassLoaderOrFail();
282 }
283
284 public PluginArtifact getPluginArtifact()
285 {
286 return pluginArtifact;
287 }
288
289 private String getInstallationStateExplanation()
290 {
291 return (null != osgiContainerManager) ? "not yet installed" : "already uninstalled";
292 }
293
294 private boolean bundleIsUsable(final String task)
295 {
296 if (null != bundle)
297 {
298 return true;
299 }
300 else
301 {
302 final String why = getInstallationStateExplanation();
303 log.warn("Cannot {} {} bundle '{}'", new Object[] { task, why, getKey()});
304 return false;
305 }
306 }
307
308 private <T> T getOrFail(final T what, final String name) throws PluginException
309 {
310 if (null == what)
311 {
312 throw new IllegalPluginStateException("Cannot use " + name + " of " + getInstallationStateExplanation() + " '"
313 + getKey() + "' from '" + pluginArtifact + "'");
314 }
315 else
316 {
317 return what;
318 }
319 }
320
321 private Bundle getBundleOrFail() throws PluginException
322 {
323 return getOrFail(bundle, "bundle");
324 }
325
326 private ClassLoader getBundleClassLoaderOrFail() throws PluginException
327 {
328 return getOrFail(bundleClassLoader, "bundleClassLoader");
329 }
330 }