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