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