1 package com.atlassian.plugin.osgi.factory;
2
3 import com.atlassian.plugin.JarPluginArtifact;
4 import com.atlassian.plugin.ModuleDescriptorFactory;
5 import com.atlassian.plugin.Plugin;
6 import com.atlassian.plugin.PluginArtifact;
7 import com.atlassian.plugin.PluginInformation;
8 import com.atlassian.plugin.PluginParseException;
9 import com.atlassian.plugin.event.PluginEventManager;
10 import com.atlassian.plugin.factories.PluginFactory;
11 import com.atlassian.plugin.impl.UnloadablePlugin;
12 import com.atlassian.plugin.loaders.classloading.DeploymentUnit;
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.atlassian.plugin.parsers.DescriptorParserFactory;
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.Arrays;
32 import java.util.HashSet;
33 import java.util.Set;
34 import java.util.jar.Manifest;
35
36
37
38
39
40
41
42
43
44 public class OsgiPluginFactory implements PluginFactory
45 {
46 private static final Logger log = LoggerFactory.getLogger(OsgiPluginFactory.class);
47
48 public interface PluginTransformerFactory
49 {
50 PluginTransformer newPluginTransformer(OsgiPersistentCache cache, SystemExports systemExports, Set<String> applicationKeys, String pluginDescriptorPath, OsgiContainerManager osgi);
51 }
52
53 public static class DefaultPluginTransformerFactory implements PluginTransformerFactory
54 {
55 public PluginTransformer newPluginTransformer(OsgiPersistentCache cache, SystemExports systemExports, Set<String> applicationKeys, String pluginDescriptorPath, OsgiContainerManager osgi)
56 {
57 return new DefaultPluginTransformer(cache, systemExports, applicationKeys, pluginDescriptorPath, osgi);
58 }
59 }
60
61 private final OsgiContainerManager osgi;
62 private final String pluginDescriptorFileName;
63 private final DescriptorParserFactory descriptorParserFactory;
64 private final PluginEventManager pluginEventManager;
65 private final Set<String> applicationKeys;
66 private final OsgiPersistentCache persistentCache;
67 private final PluginTransformerFactory pluginTransformerFactory;
68
69 private volatile PluginTransformer pluginTransformer;
70
71 private ServiceTracker moduleDescriptorFactoryTracker;
72 private final OsgiChainedModuleDescriptorFactoryCreator osgiChainedModuleDescriptorFactoryCreator;
73
74
75
76
77
78 public OsgiPluginFactory(String pluginDescriptorFileName, String applicationKey, OsgiPersistentCache persistentCache, OsgiContainerManager osgi, PluginEventManager pluginEventManager)
79 {
80 this(pluginDescriptorFileName, new HashSet<String>(Arrays.asList(applicationKey)), persistentCache, osgi, pluginEventManager);
81 }
82
83
84
85
86 public OsgiPluginFactory(String pluginDescriptorFileName, Set<String> applicationKeys, OsgiPersistentCache persistentCache, final OsgiContainerManager osgi, PluginEventManager pluginEventManager)
87 {
88 this(pluginDescriptorFileName, applicationKeys, persistentCache, osgi, pluginEventManager, new DefaultPluginTransformerFactory());
89 }
90
91
92
93
94 public OsgiPluginFactory(String pluginDescriptorFileName, Set<String> applicationKeys, OsgiPersistentCache persistentCache, final OsgiContainerManager osgi, PluginEventManager pluginEventManager, PluginTransformerFactory pluginTransformerFactory)
95 {
96 Validate.notNull(pluginDescriptorFileName, "Plugin descriptor is required");
97 Validate.notNull(osgi, "The OSGi container is required");
98 Validate.notNull(applicationKeys, "The application keys are required");
99 Validate.notNull(persistentCache, "The osgi persistent cache is required");
100 Validate.notNull(persistentCache, "The plugin event manager is required");
101 Validate.notNull(pluginTransformerFactory, "The plugin transformer factory is required");
102
103 this.osgi = osgi;
104 this.pluginDescriptorFileName = pluginDescriptorFileName;
105 this.descriptorParserFactory = new OsgiPluginXmlDescriptorParserFactory();
106 this.pluginEventManager = pluginEventManager;
107 this.applicationKeys = applicationKeys;
108 this.persistentCache = persistentCache;
109 this.osgiChainedModuleDescriptorFactoryCreator = new OsgiChainedModuleDescriptorFactoryCreator(new OsgiChainedModuleDescriptorFactoryCreator.ServiceTrackerFactory()
110 {
111 public ServiceTracker create(String className)
112 {
113 return osgi.getServiceTracker(className);
114 }
115 });
116 this.pluginTransformerFactory = pluginTransformerFactory;
117
118 }
119
120
121 private PluginTransformer getPluginTransformer()
122 {
123 if (pluginTransformer == null)
124 {
125 String exportString = (String) osgi.getBundles()[0].getHeaders()
126 .get(Constants.EXPORT_PACKAGE);
127 SystemExports exports = new SystemExports(exportString);
128 pluginTransformer = pluginTransformerFactory.newPluginTransformer(persistentCache, exports, applicationKeys, pluginDescriptorFileName, osgi);
129 }
130 return pluginTransformer;
131 }
132
133 public String canCreate(PluginArtifact pluginArtifact) throws PluginParseException
134 {
135 Validate.notNull(pluginArtifact, "The plugin artifact is required");
136
137 String pluginKey = getPluginKeyFromDescriptor(pluginArtifact);
138 if (pluginKey == null)
139 {
140 pluginKey = getPluginKeyFromManifest(pluginArtifact);
141 }
142 return pluginKey;
143 }
144
145
146
147
148
149
150 private String getPluginKeyFromManifest(PluginArtifact pluginArtifact)
151 {
152 Manifest mf = getManifest(pluginArtifact);
153 if (mf != null)
154 {
155 String key = mf.getMainAttributes().getValue(OsgiPlugin.ATLASSIAN_PLUGIN_KEY);
156 String version = mf.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
157 if (key != null)
158 {
159 if (version != null)
160 {
161 return key;
162 }
163 else
164 {
165 log.warn("Found plugin key '" + key + "' in the manifest but no bundle version, so it can't be loaded as an OsgiPlugin");
166 }
167 }
168 }
169 return null;
170 }
171
172 private Manifest getManifest(PluginArtifact pluginArtifact)
173 {
174 InputStream descriptorClassStream = pluginArtifact.getResourceAsStream("META-INF/MANIFEST.MF");
175 if (descriptorClassStream != null)
176 {
177 try
178 {
179 return new Manifest(descriptorClassStream);
180 }
181 catch (IOException e)
182 {
183 log.error("Cannot read manifest from plugin artifact " + pluginArtifact.getName(), e);
184 }
185 finally
186 {
187 IOUtils.closeQuietly(descriptorClassStream);
188 }
189 }
190 return null;
191 }
192
193 private String getPluginKeyFromDescriptor(PluginArtifact pluginArtifact)
194 {
195 String pluginKey = null;
196 InputStream descriptorStream = null;
197 try
198 {
199 descriptorStream = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
200
201 if (descriptorStream != null)
202 {
203 final DescriptorParser descriptorParser = descriptorParserFactory.getInstance(descriptorStream, applicationKeys.toArray(new String[applicationKeys.size()]));
204 if (descriptorParser.getPluginsVersion() == 2)
205 {
206 pluginKey = descriptorParser.getKey();
207 }
208 }
209 }
210 finally
211 {
212 IOUtils.closeQuietly(descriptorStream);
213 }
214 return pluginKey;
215 }
216
217
218
219
220 public Plugin create(DeploymentUnit deploymentUnit, ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
221 {
222 Validate.notNull(deploymentUnit, "The deployment unit is required");
223 return create(new JarPluginArtifact(deploymentUnit.getPath()), moduleDescriptorFactory);
224 }
225
226
227
228
229
230
231
232
233
234
235
236
237 public Plugin create(PluginArtifact pluginArtifact, ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
238 {
239 Validate.notNull(pluginArtifact, "The plugin deployment unit is required");
240 Validate.notNull(moduleDescriptorFactory, "The module descriptor factory is required");
241
242 Plugin plugin = null;
243 InputStream pluginDescriptor = null;
244 try
245 {
246 pluginDescriptor = pluginArtifact.getResourceAsStream(pluginDescriptorFileName);
247 if (pluginDescriptor != null)
248 {
249 ModuleDescriptorFactory combinedFactory = getChainedModuleDescriptorFactory(moduleDescriptorFactory, pluginArtifact);
250 DescriptorParser parser = descriptorParserFactory.getInstance(pluginDescriptor, applicationKeys.toArray(new String[applicationKeys.size()]));
251 final Plugin osgiPlugin = new OsgiPlugin(parser.getKey(), osgi, createOsgiPluginJar(pluginArtifact), pluginArtifact, pluginEventManager);
252
253
254 plugin = parser.configurePlugin(combinedFactory, osgiPlugin);
255 }
256 else
257 {
258 Manifest mf = getManifest(pluginArtifact);
259 String pluginKey = mf.getMainAttributes().getValue(OsgiPlugin.ATLASSIAN_PLUGIN_KEY);
260 String pluginVersion = mf.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
261 Validate.notEmpty(pluginKey);
262 Validate.notEmpty(pluginVersion);
263
264 plugin = new OsgiPlugin(pluginKey, osgi, pluginArtifact, pluginArtifact, pluginEventManager);
265 plugin.setKey(pluginKey);
266 plugin.setPluginsVersion(2);
267 PluginInformation info = new PluginInformation();
268 info.setVersion(pluginVersion);
269 plugin.setPluginInformation(info);
270 }
271 }
272 catch (PluginTransformationException ex)
273 {
274 return reportUnloadablePlugin(pluginArtifact.toFile(), ex);
275 }
276 finally
277 {
278 IOUtils.closeQuietly(pluginDescriptor);
279 }
280 return plugin;
281 }
282
283
284
285
286
287
288
289
290 private ModuleDescriptorFactory getChainedModuleDescriptorFactory(ModuleDescriptorFactory originalFactory, final PluginArtifact pluginArtifact)
291 {
292 return osgiChainedModuleDescriptorFactoryCreator.create(new OsgiChainedModuleDescriptorFactoryCreator.ResourceLocator()
293 {
294 public boolean doesResourceExist(String name)
295 {
296 return pluginArtifact.doesResourceExist(name);
297 }
298 }, originalFactory);
299 }
300
301 private PluginArtifact createOsgiPluginJar(PluginArtifact pluginArtifact)
302 {
303 File transformedFile = getPluginTransformer().transform(pluginArtifact, osgi.getHostComponentRegistrations());
304 return new JarPluginArtifact(transformedFile);
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 }