1 package com.atlassian.plugin.osgi.factory;
2
3 import com.atlassian.plugin.ModuleDescriptor;
4 import com.atlassian.plugin.ModuleDescriptorFactory;
5 import com.atlassian.plugin.Plugin;
6 import com.atlassian.plugin.PluginAccessor;
7 import com.atlassian.plugin.PluginArtifact;
8 import com.atlassian.plugin.PluginParseException;
9 import com.atlassian.plugin.factories.AbstractPluginFactory;
10 import com.atlassian.plugin.impl.UnloadablePlugin;
11 import com.atlassian.plugin.osgi.container.OsgiContainerManager;
12 import com.atlassian.plugin.parsers.DescriptorParser;
13 import com.atlassian.plugin.parsers.XmlDescriptorParserFactory;
14 import com.google.common.collect.ImmutableSet;
15 import org.dom4j.Element;
16 import org.osgi.framework.Constants;
17 import org.slf4j.Logger;
18 import org.slf4j.LoggerFactory;
19
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.util.function.Predicate;
23 import java.util.jar.Attributes;
24 import java.util.jar.Manifest;
25
26 import static com.atlassian.plugin.osgi.util.OsgiHeaderUtil.getManifest;
27 import static com.atlassian.plugin.osgi.util.OsgiHeaderUtil.getPluginKey;
28 import static com.atlassian.plugin.parsers.XmlDescriptorParserUtils.addModule;
29 import static com.google.common.base.Preconditions.checkNotNull;
30
31
32
33
34
35 public final class OsgiBundleFactory extends AbstractPluginFactory {
36
37 private static final Logger log = LoggerFactory.getLogger(OsgiBundleFactory.class);
38
39 private static final Predicate<Integer> IS_PLUGIN_2_OR_HIGHER = input -> input != null && input >= Plugin.VERSION_2;
40
41
42
43
44 private final OsgiContainerManager osgiContainerManager;
45
46
47
48
49 private final String pluginDescriptorFileName;
50
51
52
53
54 private final OsgiChainedModuleDescriptorFactoryCreator osgiChainedModuleDescriptorFactoryCreator;
55
56 public OsgiBundleFactory(final OsgiContainerManager osgi) {
57 this(PluginAccessor.Descriptor.FILENAME, osgi);
58 }
59
60 public OsgiBundleFactory(final String pluginDescriptorFileName, final OsgiContainerManager osgi) {
61 super(new XmlDescriptorParserFactory(), ImmutableSet.of());
62 this.pluginDescriptorFileName = checkNotNull(pluginDescriptorFileName);
63 this.osgiContainerManager = checkNotNull(osgi, "The osgi container is required");
64
65 this.osgiChainedModuleDescriptorFactoryCreator = new OsgiChainedModuleDescriptorFactoryCreator(osgiContainerManager::getServiceTracker);
66 }
67
68 @Override
69 protected InputStream getDescriptorInputStream(PluginArtifact pluginArtifact) {
70 return pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
71 }
72
73 @Override
74 protected Predicate<Integer> isValidPluginsVersion() {
75 return IS_PLUGIN_2_OR_HIGHER;
76 }
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91 public String canCreate(final PluginArtifact pluginArtifact) throws PluginParseException {
92 checkNotNull(pluginArtifact, "The plugin artifact is required");
93 final boolean isPlugin = hasDescriptor(checkNotNull(pluginArtifact));
94 final boolean hasSpring = pluginArtifact.containsSpringContext();
95 final boolean isTransformless = getPluginKeyFromManifest(checkNotNull(pluginArtifact)) != null;
96 final Manifest mf = getManifest(pluginArtifact);
97
98 String key = null;
99 if (!isTransformless && !isPlugin && mf != null) {
100 final Attributes attrs = mf.getMainAttributes();
101
102
103
104 if (attrs.containsKey(new Attributes.Name(Constants.BUNDLE_SYMBOLICNAME))) {
105 key = getPluginKey(mf);
106 }
107 }
108
109 if (key == null && (isTransformless && !hasSpring)) {
110 key = isPlugin ? getPluginKeyFromDescriptor(checkNotNull(pluginArtifact)) : getPluginKeyFromManifest(pluginArtifact);
111 }
112
113 return key;
114 }
115
116
117
118
119
120
121
122
123
124 public Plugin create(final PluginArtifact pluginArtifact, final ModuleDescriptorFactory moduleDescriptorFactory) {
125 checkNotNull(pluginArtifact, "The plugin artifact is required");
126 checkNotNull(moduleDescriptorFactory, "The module descriptor factory is required");
127
128
129 final String pluginKey = canCreate(pluginArtifact);
130 if (null == pluginKey) {
131 log.warn("Unable to load plugin from '{}'", pluginArtifact);
132 return new UnloadablePlugin("PluginArtifact has no manifest or is not a bundle: '" + pluginArtifact + "'");
133 }
134
135
136
137
138 Plugin plugin;
139 try (InputStream pluginDescriptor = pluginArtifact.getResourceAsStream(pluginDescriptorFileName)) {
140 plugin = new OsgiBundlePlugin(osgiContainerManager, pluginKey, pluginArtifact);
141 if (pluginDescriptor != null) {
142
143 ModuleDescriptorFactory combinedFactory = osgiChainedModuleDescriptorFactoryCreator.create(
144 pluginArtifact::doesResourceExist, moduleDescriptorFactory);
145
146
147
148 DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor, applications);
149 plugin = parser.configurePlugin(combinedFactory, plugin);
150 }
151 } catch (IOException ex) {
152 log.error("Unable to load plugin: {}", pluginArtifact.toFile(), ex);
153 plugin = new UnloadablePlugin("Unable to load plugin: " + ex.getMessage());
154 }
155
156 return plugin;
157 }
158
159 @Override
160 public ModuleDescriptor<?> createModule(final Plugin plugin, final Element module, final ModuleDescriptorFactory moduleDescriptorFactory) {
161 if (plugin instanceof OsgiBundlePlugin) {
162 final ModuleDescriptorFactory combinedFactory = osgiChainedModuleDescriptorFactoryCreator.create(name -> {
163
164
165
166 return false;
167 }, moduleDescriptorFactory);
168
169 return addModule(combinedFactory, plugin, module);
170 } else {
171 return null;
172 }
173 }
174
175 private String getPluginKeyFromManifest(final PluginArtifact pluginArtifact) {
176 final Manifest mf = getManifest(pluginArtifact);
177 if (mf != null) {
178 final String key = mf.getMainAttributes().getValue(OsgiPlugin.ATLASSIAN_PLUGIN_KEY);
179
180 final String version = mf.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
181 if (key != null) {
182 if (version != null) {
183 return key;
184 } else {
185 log.warn("Found plugin key '" + key + "' in the manifest but no bundle version, so it can't be loaded as an OsgiPlugin");
186 }
187 }
188 }
189 return null;
190 }
191 }