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