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