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