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