1 package com.atlassian.plugin.osgi.factory;
2
3 import com.atlassian.plugin.Application;
4 import com.atlassian.plugin.JarPluginArtifact;
5 import com.atlassian.plugin.ModuleDescriptorFactory;
6 import com.atlassian.plugin.Plugin;
7 import com.atlassian.plugin.PluginArtifact;
8 import com.atlassian.plugin.PluginInformation;
9 import com.atlassian.plugin.PluginParseException;
10 import com.atlassian.plugin.PluginPermission;
11 import com.atlassian.plugin.event.PluginEventManager;
12 import com.atlassian.plugin.factories.AbstractPluginFactory;
13 import com.atlassian.plugin.impl.UnloadablePlugin;
14 import com.atlassian.plugin.osgi.container.OsgiContainerManager;
15 import com.atlassian.plugin.osgi.container.OsgiPersistentCache;
16 import com.atlassian.plugin.osgi.factory.transform.DefaultPluginTransformer;
17 import com.atlassian.plugin.osgi.factory.transform.PluginTransformationException;
18 import com.atlassian.plugin.osgi.factory.transform.PluginTransformer;
19 import com.atlassian.plugin.osgi.factory.transform.model.SystemExports;
20 import com.atlassian.plugin.parsers.DescriptorParser;
21 import com.google.common.base.Predicate;
22 import com.google.common.collect.ImmutableSet;
23 import com.google.common.collect.Ranges;
24 import org.apache.commons.io.IOUtils;
25 import org.osgi.framework.Constants;
26 import org.osgi.util.tracker.ServiceTracker;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 import java.io.File;
31 import java.io.IOException;
32 import java.io.InputStream;
33 import java.util.Set;
34 import java.util.jar.Manifest;
35
36 import static com.atlassian.plugin.osgi.util.OsgiHeaderUtil.getAttributeWithoutValidation;
37 import static com.atlassian.plugin.osgi.util.OsgiHeaderUtil.getNonEmptyAttribute;
38 import static com.atlassian.plugin.osgi.util.OsgiHeaderUtil.getValidatedAttribute;
39 import static com.google.common.base.Preconditions.checkNotNull;
40
41
42
43
44
45
46
47
48
49 public final class OsgiPluginFactory extends AbstractPluginFactory
50 {
51 private static final Logger log = LoggerFactory.getLogger(OsgiPluginFactory.class);
52
53 public interface PluginTransformerFactory
54 {
55 PluginTransformer newPluginTransformer(OsgiPersistentCache cache, SystemExports systemExports, Set<Application> applicationKeys, String pluginDescriptorPath, OsgiContainerManager osgi);
56 }
57
58 public static class DefaultPluginTransformerFactory implements PluginTransformerFactory
59 {
60 public PluginTransformer newPluginTransformer(OsgiPersistentCache cache, SystemExports systemExports, Set<Application> applicationKeys, String pluginDescriptorPath, OsgiContainerManager osgi)
61 {
62 return new DefaultPluginTransformer(cache, systemExports, applicationKeys, pluginDescriptorPath, osgi);
63 }
64 }
65
66 private final OsgiContainerManager osgi;
67 private final String pluginDescriptorFileName;
68 private final PluginEventManager pluginEventManager;
69 private final Set<Application> applications;
70 private final OsgiPersistentCache persistentCache;
71 private final PluginTransformerFactory pluginTransformerFactory;
72
73 private volatile PluginTransformer pluginTransformer;
74
75 private final OsgiChainedModuleDescriptorFactoryCreator osgiChainedModuleDescriptorFactoryCreator;
76
77
78
79
80 public OsgiPluginFactory(String pluginDescriptorFileName, Set<Application> applications, OsgiPersistentCache persistentCache, final OsgiContainerManager osgi, PluginEventManager pluginEventManager)
81 {
82 this(pluginDescriptorFileName, applications, persistentCache, osgi, pluginEventManager, new DefaultPluginTransformerFactory());
83 }
84
85
86
87
88 public OsgiPluginFactory(String pluginDescriptorFileName, Set<Application> applications, OsgiPersistentCache persistentCache, final OsgiContainerManager osgi, PluginEventManager pluginEventManager, PluginTransformerFactory pluginTransformerFactory)
89 {
90 super(new OsgiPluginXmlDescriptorParserFactory(), applications);
91 this.pluginDescriptorFileName = checkNotNull(pluginDescriptorFileName, "Plugin descriptor is required");
92 this.osgi = checkNotNull(osgi, "The OSGi container is required");
93 this.applications = checkNotNull(applications, "Applications is required!");
94 this.persistentCache = checkNotNull(persistentCache, "The osgi persistent cache is required");
95 this.pluginEventManager = checkNotNull(pluginEventManager, "The plugin event manager is required");
96 this.pluginTransformerFactory = checkNotNull(pluginTransformerFactory, "The plugin transformer factory is required");
97 this.osgiChainedModuleDescriptorFactoryCreator = new OsgiChainedModuleDescriptorFactoryCreator(new OsgiChainedModuleDescriptorFactoryCreator.ServiceTrackerFactory()
98 {
99 public ServiceTracker create(String className)
100 {
101 return osgi.getServiceTracker(className);
102 }
103 });
104 }
105
106 private PluginTransformer getPluginTransformer()
107 {
108 if (pluginTransformer == null)
109 {
110 String exportString = (String) osgi.getBundles()[0].getHeaders()
111 .get(Constants.EXPORT_PACKAGE);
112 SystemExports exports = new SystemExports(exportString);
113 pluginTransformer = pluginTransformerFactory.newPluginTransformer(persistentCache, exports, applications, pluginDescriptorFileName, osgi);
114 }
115 return pluginTransformer;
116 }
117
118 public String canCreate(PluginArtifact pluginArtifact) throws PluginParseException
119 {
120 if (hasDescriptor(checkNotNull(pluginArtifact)))
121 {
122 return getPluginKeyFromDescriptor(checkNotNull(pluginArtifact));
123 }
124 else
125 {
126 return getPluginKeyFromManifest(pluginArtifact);
127 }
128 }
129
130 @Override
131 protected InputStream getDescriptorInputStream(PluginArtifact pluginArtifact)
132 {
133 return pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
134 }
135
136 @Override
137 protected Predicate<Integer> isValidPluginsVersion()
138 {
139 return Ranges.singleton(Plugin.VERSION_2);
140 }
141
142
143
144
145
146
147 private String getPluginKeyFromManifest(PluginArtifact pluginArtifact)
148 {
149 final Manifest mf = getManifest(pluginArtifact);
150 if (mf != null)
151 {
152 final String key = mf.getMainAttributes().getValue(OsgiPlugin.ATLASSIAN_PLUGIN_KEY);
153 final String version = mf.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
154 if (key != null)
155 {
156 if (version != null)
157 {
158 return key;
159 }
160 else
161 {
162 log.warn("Found plugin key '" + key + "' in the manifest but no bundle version, so it can't be loaded as an OsgiPlugin");
163 }
164 }
165 }
166 return null;
167 }
168
169 private Manifest getManifest(PluginArtifact pluginArtifact)
170 {
171 InputStream descriptorClassStream = pluginArtifact.getResourceAsStream("META-INF/MANIFEST.MF");
172 if (descriptorClassStream != null)
173 {
174 try
175 {
176 return new Manifest(descriptorClassStream);
177 }
178 catch (IOException e)
179 {
180 log.error("Cannot read manifest from plugin artifact " + pluginArtifact.getName(), e);
181 }
182 finally
183 {
184 IOUtils.closeQuietly(descriptorClassStream);
185 }
186 }
187 return null;
188 }
189
190
191
192
193
194
195
196
197
198
199
200
201
202 public Plugin create(PluginArtifact pluginArtifact, ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
203 {
204 checkNotNull(pluginArtifact, "The plugin deployment unit is required");
205 checkNotNull(moduleDescriptorFactory, "The module descriptor factory is required");
206
207 Plugin plugin = null;
208 InputStream pluginDescriptor = null;
209 try
210 {
211 pluginDescriptor = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
212 if (pluginDescriptor != null)
213 {
214 ModuleDescriptorFactory combinedFactory = getChainedModuleDescriptorFactory(moduleDescriptorFactory, pluginArtifact);
215 DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor, applications);
216
217 final PluginArtifact artifactToInstall;
218
219
220 final String pluginKeyFromManifest = getPluginKeyFromManifest(pluginArtifact);
221 if (pluginKeyFromManifest == null)
222 {
223 log.debug("Plugin key NOT found in manifest at entry {}, undergoing transformation", OsgiPlugin.ATLASSIAN_PLUGIN_KEY);
224 artifactToInstall = createOsgiPluginJar(pluginArtifact);
225 }
226 else
227 {
228 log.debug("Plugin key found in manifest at entry {}, skipping transformation for '{}'", OsgiPlugin.ATLASSIAN_PLUGIN_KEY, pluginKeyFromManifest);
229 artifactToInstall = pluginArtifact;
230 }
231
232 final Plugin osgiPlugin = new OsgiPlugin(parser.getKey(), osgi, artifactToInstall, pluginArtifact, pluginEventManager);
233
234
235 plugin = parser.configurePlugin(combinedFactory, osgiPlugin);
236 }
237 else
238 {
239 Manifest mf = getManifest(pluginArtifact);
240 plugin = extractOsgiPlugin(pluginArtifact, mf, osgi, pluginEventManager);
241 plugin.setPluginInformation(extractOsgiPluginInformation(mf));
242 }
243 }
244 catch (PluginTransformationException ex)
245 {
246 return reportUnloadablePlugin(pluginArtifact.toFile(), ex);
247 }
248 finally
249 {
250 IOUtils.closeQuietly(pluginDescriptor);
251 }
252 return plugin;
253 }
254
255 private static Plugin extractOsgiPlugin(PluginArtifact pluginArtifact, Manifest mf, final OsgiContainerManager osgi, final PluginEventManager pluginEventManager)
256 {
257 Plugin plugin;
258 String pluginKey = getNonEmptyAttribute(mf, OsgiPlugin.ATLASSIAN_PLUGIN_KEY);
259 String bundleName = getAttributeWithoutValidation(mf, Constants.BUNDLE_NAME);
260
261 plugin = new OsgiPlugin(pluginKey, osgi, pluginArtifact, pluginArtifact, pluginEventManager);
262 plugin.setKey(pluginKey);
263 plugin.setPluginsVersion(2);
264 plugin.setName(bundleName);
265 return plugin;
266 }
267
268 private PluginInformation extractOsgiPluginInformation(final Manifest manifest) {
269 String pluginVersion = getValidatedAttribute(manifest, Constants.BUNDLE_VERSION);
270 String vendorName = getAttributeWithoutValidation(manifest, Constants.BUNDLE_VENDOR);
271 String bundleDescription = getAttributeWithoutValidation(manifest, Constants.BUNDLE_DESCRIPTION);
272 PluginInformation info = new PluginInformation();
273 info.setVersion(pluginVersion);
274 info.setDescription(bundleDescription);
275 info.setVendorName(vendorName);
276 info.setPermissions(ImmutableSet.of(PluginPermission.EXECUTE_JAVA));
277 return info;
278 }
279
280
281
282
283
284
285
286
287
288 private ModuleDescriptorFactory getChainedModuleDescriptorFactory(ModuleDescriptorFactory originalFactory, final PluginArtifact pluginArtifact)
289 {
290 return osgiChainedModuleDescriptorFactoryCreator.create(new OsgiChainedModuleDescriptorFactoryCreator.ResourceLocator()
291 {
292 public boolean doesResourceExist(String name)
293 {
294 return pluginArtifact.doesResourceExist(name);
295 }
296 }, originalFactory);
297 }
298
299 private PluginArtifact createOsgiPluginJar(PluginArtifact pluginArtifact)
300 {
301 File transformedFile = getPluginTransformer().transform(pluginArtifact, osgi.getHostComponentRegistrations());
302
303
304 return new JarPluginArtifact(transformedFile, true);
305 }
306
307 private Plugin reportUnloadablePlugin(File file, Exception e)
308 {
309 log.error("Unable to load plugin: " + file, e);
310
311 UnloadablePlugin plugin = new UnloadablePlugin();
312 plugin.setErrorText("Unable to load plugin: " + e.getMessage());
313 return plugin;
314 }
315 }