View Javadoc

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.JarPluginArtifact;
6   import com.atlassian.plugin.PluginArtifact;
7   import org.apache.commons.lang.Validate;
8   import org.apache.log4j.Logger;
9   
10  import java.io.*;
11  import java.util.List;
12  import java.util.Map;
13  import java.util.ArrayList;
14  import java.util.Collections;
15  import java.util.zip.ZipEntry;
16  import java.util.zip.ZipInputStream;
17  import java.util.zip.ZipOutputStream;
18  
19  /**
20   * Default implementation of plugin transformation that uses stages to convert a plain JAR into an OSGi bundle.
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  
29      /**
30       * Constructs a transformer with the default stages
31       *
32       * @param pluginDescriptorPath The path to the plugin descriptor
33       * @since 2.2.0
34       */
35      public DefaultPluginTransformer(String pluginDescriptorPath)
36      {
37          this(pluginDescriptorPath, new ArrayList<TransformStage>()
38          {{
39              add(new AddBundleOverridesStage());
40              add(new ComponentImportSpringStage());
41              add(new ComponentSpringStage());
42              add(new HostComponentSpringStage());
43              add(new ModuleTypeSpringStage());
44              add(new GenerateManifestStage());
45          }});
46      }
47  
48      /**
49       * Constructs a transformer and its stages
50       *
51       * @param pluginDescriptorPath The descriptor path
52       * @param stages A set of stages
53       * @since 2.2.0
54       */
55      public DefaultPluginTransformer(String pluginDescriptorPath, List<TransformStage> stages)
56      {
57          Validate.notNull(pluginDescriptorPath, "The plugin descriptor path is required");
58          Validate.notNull(stages, "A list of stages is required");
59          this.pluginDescriptorPath = pluginDescriptorPath;
60          this.stages = Collections.unmodifiableList(new ArrayList<TransformStage>(stages));
61      }
62  
63      /**
64       * Transforms the file into an OSGi bundle
65       *
66       * @param pluginJar The plugin jar
67       * @param regs      The list of registered host components
68       * @return The new OSGi-enabled plugin jar
69       * @throws PluginTransformationException If anything goes wrong
70       */
71      public File transform(File pluginJar, List<HostComponentRegistration> regs) throws PluginTransformationException
72      {
73          return transform(new JarPluginArtifact(pluginJar), regs);
74      }
75  
76      /**
77       * Transforms the file into an OSGi bundle
78       *
79       * @param pluginArtifact The plugin artifact, usually a jar
80       * @param regs      The list of registered host components
81       * @return The new OSGi-enabled plugin jar
82       * @throws PluginTransformationException If anything goes wrong
83       */
84      public File transform(PluginArtifact pluginArtifact, List<HostComponentRegistration> regs) throws PluginTransformationException
85      {
86          Validate.notNull(pluginArtifact, "The plugin artifact is required");
87          Validate.notNull(regs, "The host component registrations are required");
88          TransformContext context = new TransformContext(regs, pluginArtifact, pluginDescriptorPath);
89          for (TransformStage stage : stages)
90          {
91              stage.execute(context);
92          }
93  
94          // Create a new jar by overriding the specified files
95          try
96          {
97              if (log.isDebugEnabled())
98              {
99                  StringBuilder sb = new StringBuilder();
100                 sb.append("Overriding files in ").append(pluginArtifact.toString()).append(":\n");
101                 for (Map.Entry<String, byte[]> entry : context.getFileOverrides().entrySet())
102                 {
103                     sb.append("==").append(entry.getKey()).append("==\n");
104                     sb.append(new String(entry.getValue()));
105                 }
106                 log.debug(sb.toString());
107             }
108             return addFilesToExistingZip(pluginArtifact.toFile(), context.getFileOverrides());
109         }
110         catch (IOException e)
111         {
112             throw new PluginTransformationException("Unable to add files to plugin jar");
113         }
114     }
115 
116 
117     /**
118      * Creates a new jar by overriding the specified files in the existing one
119      *
120      * @param zipFile The existing zip file
121      * @param files   The files to override
122      * @return The new zip
123      * @throws IOException If there are any problems processing the streams
124      */
125     static File addFilesToExistingZip(File zipFile,
126                                       Map<String, byte[]> files) throws IOException
127     {
128         // get a temp file
129         File tempFile = File.createTempFile(zipFile.getName(), null);
130         // delete it, otherwise you cannot rename your existing zip to it.
131         byte[] buf = new byte[1024];
132 
133         ZipInputStream zin = new ZipInputStream(new FileInputStream(zipFile));
134         ZipOutputStream out = new ZipOutputStream(new FileOutputStream(tempFile));
135 
136         ZipEntry entry = zin.getNextEntry();
137         while (entry != null)
138         {
139             String name = entry.getName();
140             if (!files.containsKey(name))
141             {
142                 // Add ZIP entry to output stream.
143                 out.putNextEntry(new ZipEntry(name));
144                 // Transfer bytes from the ZIP file to the output file
145                 int len;
146                 while ((len = zin.read(buf)) > 0)
147                     out.write(buf, 0, len);
148             }
149             entry = zin.getNextEntry();
150         }
151         // Close the streams
152         zin.close();
153         // Compress the files
154         for (Map.Entry<String, byte[]> fentry : files.entrySet())
155         {
156             InputStream in = new ByteArrayInputStream(fentry.getValue());
157             // Add ZIP entry to output stream.
158             out.putNextEntry(new ZipEntry(fentry.getKey()));
159             // Transfer bytes from the file to the ZIP file
160             int len;
161             while ((len = in.read(buf)) > 0)
162             {
163                 out.write(buf, 0, len);
164             }
165             // Complete the entry
166             out.closeEntry();
167             in.close();
168         }
169         // Complete the ZIP file
170         out.close();
171         return tempFile;
172     }
173 
174     
175 }