1 package com.atlassian.plugin.osgi.factory.transform;
2
3 import static com.atlassian.plugin.osgi.factory.transform.JarUtils.getEntries;
4 import static com.atlassian.plugin.osgi.factory.transform.JarUtils.getEntry;
5
6 import com.atlassian.plugin.PluginArtifact;
7 import com.atlassian.plugin.PluginParseException;
8 import com.atlassian.plugin.osgi.container.OsgiContainerManager;
9 import com.atlassian.plugin.osgi.factory.transform.model.ComponentImport;
10 import com.atlassian.plugin.osgi.factory.transform.model.SystemExports;
11 import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
12 import com.atlassian.plugin.parsers.XmlDescriptorParser;
13
14 import org.apache.commons.io.IOUtils;
15 import org.apache.commons.lang.Validate;
16 import org.dom4j.Document;
17 import org.dom4j.Element;
18
19 import com.google.common.collect.ImmutableMap;
20 import org.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
22
23 import java.io.File;
24 import java.io.InputStream;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
32 import java.util.jar.JarEntry;
33 import java.util.jar.Manifest;
34
35
36
37
38
39
40 public final class TransformContext
41 {
42 private final Manifest manifest;
43 private final List<HostComponentRegistration> regs;
44 private final Map<String, byte[]> fileOverrides;
45 private final Map<String, String> bndInstructions;
46 private final Document descriptorDocument;
47 private final List<String> extraImports;
48 private final List<String> extraExports;
49 private final Set<String> bundleClassPathJars;
50 private final PluginArtifact pluginArtifact;
51 private final Map<String, ComponentImport> componentImports;
52 private final SystemExports systemExports;
53 private final Set<String> applicationKeys;
54 private boolean shouldRequireSpring = false;
55 private final OsgiContainerManager osgiContainerManager;
56 private final Set<HostComponentRegistration> requiredHostComponents;
57
58 private static final Logger LOG = LoggerFactory.getLogger(TransformContext.class);
59
60
61
62
63 private final Map<String, String> beanSourceMap = new HashMap<String, String>();
64
65 public TransformContext(final List<HostComponentRegistration> regs, final SystemExports systemExports, final PluginArtifact pluginArtifact, final Set<String> applicationKeys, final String descriptorPath, final OsgiContainerManager osgiContainerManager)
66 {
67 this.osgiContainerManager = osgiContainerManager;
68 Validate.notNull(pluginArtifact, "The plugin artifact must be specified");
69 Validate.notNull(descriptorPath, "The plugin descriptor path must be specified");
70 Validate.notNull(systemExports, "The system exports must be specified");
71
72 this.regs = regs;
73 this.systemExports = systemExports;
74 this.pluginArtifact = pluginArtifact;
75 this.applicationKeys = (applicationKeys == null ? Collections.<String> emptySet() : applicationKeys);
76
77 manifest = JarUtils.getManifest(pluginArtifact.toFile());
78 fileOverrides = new HashMap<String, byte[]>();
79 bndInstructions = new HashMap<String, String>();
80 descriptorDocument = retrieveDocFromJar(pluginArtifact, descriptorPath);
81 extraImports = new ArrayList<String>();
82 extraExports = new ArrayList<String>();
83 bundleClassPathJars = new HashSet<String>();
84
85 componentImports = parseComponentImports(descriptorDocument);
86 requiredHostComponents = new HashSet<HostComponentRegistration>();
87 }
88
89 public File getPluginFile()
90 {
91 return pluginArtifact.toFile();
92 }
93
94 public PluginArtifact getPluginArtifact()
95 {
96 return pluginArtifact;
97 }
98
99 public List<HostComponentRegistration> getHostComponentRegistrations()
100 {
101 return regs;
102 }
103
104 public Map<String, byte[]> getFileOverrides()
105 {
106 return fileOverrides;
107 }
108
109 public Map<String, String> getBndInstructions()
110 {
111 return bndInstructions;
112 }
113
114 public Document getDescriptorDocument()
115 {
116 return descriptorDocument;
117 }
118
119 public Manifest getManifest()
120 {
121 return manifest;
122 }
123
124 public List<String> getExtraImports()
125 {
126 return extraImports;
127 }
128
129 public List<String> getExtraExports()
130 {
131 return extraExports;
132 }
133
134 public void addBundleClasspathJar(String classpath)
135 {
136 bundleClassPathJars.add(classpath);
137 }
138
139 public Set<String> getBundleClassPathJars()
140 {
141 return Collections.unmodifiableSet(bundleClassPathJars);
142 }
143
144 public Map<String, ComponentImport> getComponentImports()
145 {
146 return componentImports;
147 }
148
149 public SystemExports getSystemExports()
150 {
151 return systemExports;
152 }
153
154 public Set<String> getApplicationKeys()
155 {
156 return applicationKeys;
157 }
158
159 public boolean shouldRequireSpring()
160 {
161 return shouldRequireSpring;
162 }
163
164 public void setShouldRequireSpring(final boolean shouldRequireSpring)
165 {
166 this.shouldRequireSpring = shouldRequireSpring;
167 if (shouldRequireSpring)
168 {
169 getFileOverrides().put("META-INF/spring/", new byte[0]);
170 }
171 }
172
173 public OsgiContainerManager getOsgiContainerManager()
174 {
175 return osgiContainerManager;
176 }
177
178 public Iterable<JarEntry> getPluginJarEntries()
179 {
180 return getEntries(pluginArtifact.toFile());
181 }
182
183 public JarEntry getPluginJarEntry(final String path)
184 {
185 return getEntry(pluginArtifact.toFile(), path);
186 }
187
188 public void addRequiredHostComponent(final HostComponentRegistration hostComponent)
189 {
190 requiredHostComponents.add(hostComponent);
191 }
192
193 public Set<HostComponentRegistration> getRequiredHostComponents()
194 {
195 return requiredHostComponents;
196 }
197
198
199
200
201
202
203
204
205 public void trackBean(final String name, final String source) throws PluginTransformationException
206 {
207 Validate.notNull(name, "empty bean name");
208 Validate.notNull(source, "source of bean is required");
209
210
211 if (beanSourceMap.containsKey(name))
212 {
213 final String message = String.format("The bean identifier '%s' is used by two different beans from %s and %s."
214 + "This is a bad practice and may not be supported in newer plugin framework version.",
215 name, source, beanSourceMap.get(name));
216
217 LOG.warn(message);
218
219 }
220
221
222 beanSourceMap.put(name, source);
223 }
224
225
226
227
228
229
230
231 public boolean beanExists(final String name)
232 {
233 return beanSourceMap.containsKey(name);
234 }
235
236
237
238
239
240 private static Map<String, ComponentImport> parseComponentImports(final Document descriptorDocument)
241 {
242 final Map<String, ComponentImport> componentImports = new HashMap<String, ComponentImport>();
243 @SuppressWarnings("unchecked")
244 final List<Element> elements = descriptorDocument.getRootElement().elements("component-import");
245 for (final Element component : elements)
246 {
247 final ComponentImport ci = new ComponentImport(component);
248 componentImports.put(ci.getKey(), ci);
249 }
250 return ImmutableMap.copyOf(componentImports);
251 }
252
253 private static Document retrieveDocFromJar(final PluginArtifact pluginArtifact, final String descriptorPath) throws PluginTransformationException
254 {
255 InputStream stream = null;
256 try
257 {
258 stream = pluginArtifact.getResourceAsStream(descriptorPath);
259 if (stream == null)
260 {
261 throw new PluginTransformationException("Unable to access descriptor " + descriptorPath);
262 }
263 return new DocumentExposingDescriptorParser(stream).getDocument();
264 }
265 finally
266 {
267 IOUtils.closeQuietly(stream);
268 }
269 }
270
271
272
273
274 private static class DocumentExposingDescriptorParser extends XmlDescriptorParser
275 {
276
277
278
279
280 DocumentExposingDescriptorParser(final InputStream source) throws PluginParseException
281 {
282
283 super(source, (String) null);
284 }
285
286 @Override
287 public Document getDocument()
288 {
289 return super.getDocument();
290 }
291 }
292 }