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