1 package com.atlassian.plugin.osgi.factory.transform;
2
3 import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
4 import com.atlassian.plugin.osgi.factory.transform.stage.*;
5 import com.atlassian.plugin.osgi.factory.transform.model.SystemExports;
6 import com.atlassian.plugin.osgi.container.OsgiPersistentCache;
7 import com.atlassian.plugin.JarPluginArtifact;
8 import com.atlassian.plugin.PluginArtifact;
9 import org.apache.commons.lang.Validate;
10 import org.apache.commons.io.IOUtils;
11 import org.apache.log4j.Logger;
12
13 import java.io.*;
14 import java.util.*;
15 import java.util.zip.ZipEntry;
16 import java.util.zip.ZipInputStream;
17 import java.util.zip.ZipOutputStream;
18
19
20
21
22 public class DefaultPluginTransformer implements PluginTransformer
23 {
24 private static final Logger log = Logger.getLogger(DefaultPluginTransformer.class);
25
26 private final String pluginDescriptorPath;
27 private final List<TransformStage> stages;
28 private final File bundleCache;
29 private final SystemExports systemExports;
30 private final Set<String> applicationKeys;
31
32
33
34
35
36
37
38
39
40 public DefaultPluginTransformer(OsgiPersistentCache cache, SystemExports systemExports, Set<String> applicationKeys, String pluginDescriptorPath)
41 {
42 this(cache, systemExports, applicationKeys, pluginDescriptorPath, new ArrayList<TransformStage>()
43 {{
44 add(new AddBundleOverridesStage());
45 add(new ComponentImportSpringStage());
46 add(new ComponentSpringStage());
47 add(new HostComponentSpringStage());
48 add(new ModuleTypeSpringStage());
49 add(new ScanDescriptorForHostClassesStage());
50 add(new GenerateManifestStage());
51 }});
52 }
53
54
55
56
57
58
59
60
61
62
63 public DefaultPluginTransformer(OsgiPersistentCache cache, SystemExports systemExports, Set<String> applicationKeys, String pluginDescriptorPath, List<TransformStage> stages)
64 {
65 Validate.notNull(pluginDescriptorPath, "The plugin descriptor path is required");
66 Validate.notNull(stages, "A list of stages is required");
67 this.pluginDescriptorPath = pluginDescriptorPath;
68 this.stages = Collections.unmodifiableList(new ArrayList<TransformStage>(stages));
69 this.bundleCache = cache.getTransformedPluginCache();
70 this.systemExports = systemExports;
71 this.applicationKeys = applicationKeys;
72
73 }
74
75
76
77
78
79
80
81
82
83 public File transform(File pluginJar, List<HostComponentRegistration> regs) throws PluginTransformationException
84 {
85 return transform(new JarPluginArtifact(pluginJar), regs);
86 }
87
88
89
90
91
92
93
94
95
96 public File transform(PluginArtifact pluginArtifact, List<HostComponentRegistration> regs) throws PluginTransformationException
97 {
98 Validate.notNull(pluginArtifact, "The plugin artifact is required");
99 Validate.notNull(regs, "The host component registrations are required");
100
101 File artifactFile = pluginArtifact.toFile();
102
103
104 File cachedPlugin = getFromCache(artifactFile);
105 if (cachedPlugin != null)
106 {
107 return cachedPlugin;
108 }
109
110 TransformContext context = new TransformContext(regs, systemExports, pluginArtifact, applicationKeys, pluginDescriptorPath);
111 for (TransformStage stage : stages)
112 {
113 stage.execute(context);
114 }
115
116
117 try
118 {
119 if (log.isDebugEnabled())
120 {
121 StringBuilder sb = new StringBuilder();
122 sb.append("Overriding files in ").append(pluginArtifact.toString()).append(":\n");
123 for (Map.Entry<String, byte[]> entry : context.getFileOverrides().entrySet())
124 {
125 sb.append("==").append(entry.getKey()).append("==\n");
126
127
128
129 sb.append(new String(entry.getValue()));
130 }
131 log.debug(sb.toString());
132 }
133 return addFilesToExistingZip(artifactFile, context.getFileOverrides());
134 }
135 catch (IOException e)
136 {
137 throw new PluginTransformationException("Unable to add files to plugin jar");
138 }
139 }
140
141 private File getFromCache(File artifact)
142 {
143 String name = generateCacheName(artifact);
144 for (File child : bundleCache.listFiles())
145 {
146 if (child.getName().equals(name))
147 return child;
148 }
149 return null;
150 }
151
152
153
154
155
156
157 static String generateCacheName(File file)
158 {
159 int dotPos = file.getName().lastIndexOf('.');
160 if (dotPos > 0 && file.getName().length() - 1 > dotPos)
161 {
162 return file.getName().substring(0, dotPos) + "_" + file.lastModified() + file.getName().substring(dotPos);
163 }
164 else
165 {
166 return file.getName() + "_" + file.lastModified();
167 }
168 }
169
170
171
172
173
174
175
176
177
178
179 File addFilesToExistingZip(File zipFile,
180 Map<String, byte[]> files) throws IOException
181 {
182
183 File tempFile = new File(bundleCache, generateCacheName(zipFile));
184
185 byte[] buf = new byte[1024];
186
187
188 ZipInputStream zin = null;
189 ZipOutputStream out = null;
190 try
191 {
192 zin = new ZipInputStream(new FileInputStream(zipFile));
193 out = new ZipOutputStream(new FileOutputStream(tempFile));
194
195 ZipEntry entry = zin.getNextEntry();
196 while (entry != null)
197 {
198 String name = entry.getName();
199 if (!files.containsKey(name))
200 {
201
202 out.putNextEntry(new ZipEntry(name));
203
204 int len;
205 while ((len = zin.read(buf)) > 0)
206 out.write(buf, 0, len);
207 }
208 entry = zin.getNextEntry();
209 }
210
211 zin.close();
212
213 for (Map.Entry<String, byte[]> fentry : files.entrySet())
214 {
215 InputStream in = null;
216 try
217 {
218 in = new ByteArrayInputStream(fentry.getValue());
219
220 out.putNextEntry(new ZipEntry(fentry.getKey()));
221
222 int len;
223 while ((len = in.read(buf)) > 0)
224 {
225 out.write(buf, 0, len);
226 }
227
228 out.closeEntry();
229 }
230 finally
231 {
232 IOUtils.closeQuietly(in);
233 }
234 }
235
236 out.close();
237 }
238 finally
239 {
240
241 IOUtils.closeQuietly(zin);
242 IOUtils.closeQuietly(out);
243 }
244 return tempFile;
245 }
246
247
248 }