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