1   package com.atlassian.plugin.osgi.factory.transform;
2   
3   import java.io.File;
4   import java.io.IOException;
5   import java.io.InputStream;
6   import java.util.ArrayList;
7   import java.util.Collections;
8   import java.util.Enumeration;
9   import java.util.HashMap;
10  import java.util.List;
11  import java.util.Map;
12  import java.util.Set;
13  import java.util.jar.JarEntry;
14  import java.util.jar.JarFile;
15  import java.util.jar.Manifest;
16  
17  import org.apache.commons.io.IOUtils;
18  import org.apache.commons.lang.Validate;
19  import org.dom4j.Document;
20  import org.dom4j.Element;
21  
22  import com.atlassian.plugin.PluginArtifact;
23  import com.atlassian.plugin.PluginParseException;
24  import com.atlassian.plugin.osgi.factory.transform.model.ComponentImport;
25  import com.atlassian.plugin.osgi.factory.transform.model.SystemExports;
26  import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
27  import com.atlassian.plugin.parsers.XmlDescriptorParser;
28  
29  /**
30   * The transform context containing any configuration necessary to enact a JAR transformation
31   *
32   * @since 2.2.0
33   */
34  public class TransformContext
35  {
36  //    private final JarFile pluginJar;
37      private final Manifest manifest;
38      private final List<HostComponentRegistration> regs;
39      private final Map<String, byte[]> fileOverrides;
40      private final Map<String, String> bndInstructions;
41      private final Document descriptorDocument;
42      private final List<String> extraImports;
43      private final List<String> extraExports;
44      private final PluginArtifact pluginArtifact;
45      private final Map<String, ComponentImport> componentImports;
46      private final SystemExports systemExports;
47      private final Set<String> applicationKeys;
48      private boolean shouldRequireSpring = false;
49  
50      public TransformContext(final List<HostComponentRegistration> regs, final SystemExports systemExports,
51                              final PluginArtifact pluginArtifact, final Set<String> applicationKeys, final String descriptorPath)
52      {
53          Validate.notNull(pluginArtifact, "The plugin artifact must be specified");
54          Validate.notNull(descriptorPath, "The plugin descriptor path must be specified");
55          Validate.notNull(systemExports, "The system exports must be specified");
56  
57          this.regs = regs;
58          this.systemExports = systemExports;
59          this.pluginArtifact = pluginArtifact;
60          this.applicationKeys = (applicationKeys == null ? Collections.<String>emptySet() : applicationKeys);
61  
62          JarFile jarFile = null;
63          try
64          {
65              jarFile = new JarFile(pluginArtifact.toFile());
66              this.manifest = jarFile.getManifest();
67          }
68          catch (final IOException e)
69          {
70              throw new IllegalArgumentException("File must be a jar", e);
71          } finally
72          {
73              closeJarQuietly(jarFile);
74          }
75          fileOverrides = new HashMap<String, byte[]>();
76          bndInstructions = new HashMap<String, String>();
77          this.descriptorDocument = retrieveDocFromJar(pluginArtifact, descriptorPath);
78          this.extraImports = new ArrayList<String>();
79          this.extraExports = new ArrayList<String>();
80  
81          this.componentImports = Collections.unmodifiableMap(parseComponentImports(descriptorDocument));
82      }
83  
84      private Map<String, ComponentImport> parseComponentImports(final Document descriptorDocument)
85      {
86          final Map<String,ComponentImport> componentImports = new HashMap<String,ComponentImport>();
87          final List<Element> elements = descriptorDocument.getRootElement().elements("component-import");
88          for (final Element component : elements)
89          {
90              final ComponentImport ci = new ComponentImport(component);
91              componentImports.put(ci.getKey(), ci);
92          }
93          return componentImports;
94      }
95  
96      private Document retrieveDocFromJar(final PluginArtifact pluginArtifact, final String descriptorPath) throws PluginTransformationException
97      {
98          Document document;
99          InputStream stream = null;
100         try
101         {
102             stream = pluginArtifact.getResourceAsStream(descriptorPath);
103             if (stream == null)
104             {
105                 throw new PluginTransformationException("Unable to access descriptor " + descriptorPath);
106             }
107             final DocumentExposingDescriptorParser parser = new DocumentExposingDescriptorParser(stream);
108             document = parser.getDocument();
109         }
110         finally
111         {
112             IOUtils.closeQuietly(stream);
113         }
114         return document;
115     }
116 
117     public File getPluginFile()
118     {
119         return pluginArtifact.toFile();
120     }
121 
122     public PluginArtifact getPluginArtifact()
123     {
124         return pluginArtifact;
125     }
126 
127     public List<HostComponentRegistration> getHostComponentRegistrations()
128     {
129         return regs;
130     }
131 
132     public Map<String, byte[]> getFileOverrides()
133     {
134         return fileOverrides;
135     }
136 
137     public Map<String, String> getBndInstructions()
138     {
139         return bndInstructions;
140     }
141 
142     public Document getDescriptorDocument()
143     {
144         return descriptorDocument;
145     }
146 
147     public Manifest getManifest()
148     {
149         return manifest;
150     }
151 
152     public List<String> getExtraImports()
153     {
154         return extraImports;
155     }
156 
157     public List<String> getExtraExports()
158     {
159         return extraExports;
160     }
161 
162     public Map<String, ComponentImport> getComponentImports()
163     {
164         return componentImports;
165     }
166 
167     public SystemExports getSystemExports()
168     {
169         return systemExports;
170     }
171 
172     public Set<String> getApplicationKeys()
173     {
174         return applicationKeys;
175     }
176 
177     public boolean shouldRequireSpring()
178     {
179         return shouldRequireSpring;
180     }
181 
182     public void setShouldRequireSpring(final boolean shouldRequireSpring)
183     {
184         this.shouldRequireSpring = shouldRequireSpring;
185         if (shouldRequireSpring)
186         {
187             getFileOverrides().put("META-INF/spring/", new byte[0]);
188         }
189 
190     }
191 
192     private static class DocumentExposingDescriptorParser extends XmlDescriptorParser
193     {
194         /**
195          * @throws com.atlassian.plugin.PluginParseException
196          *          if there is a problem reading the descriptor from the XML {@link java.io.InputStream}.
197          */
198         public DocumentExposingDescriptorParser(final InputStream source) throws PluginParseException
199         {
200             // A null application key is fine here as we are only interested in the parsed document
201             super(source, null);
202         }
203 
204         @Override
205         public Document getDocument()
206         {
207             return super.getDocument();
208         }
209     }
210 
211     public List<JarEntry> getPluginJarEntries()
212     {
213         final List<JarEntry> list = new ArrayList<JarEntry>();
214         JarFile jarFile = null;
215         try
216         {
217             jarFile = new JarFile(pluginArtifact.toFile());
218             final Enumeration<JarEntry> entries = jarFile.entries();
219             for (final Enumeration<JarEntry> e = entries; e.hasMoreElements();)
220             {
221                 final JarEntry entry = e.nextElement();
222                 list.add(entry);
223             }
224             return list;
225         }
226         catch (final IOException e)
227         {
228             throw new IllegalArgumentException("File must be a jar", e);
229         } finally
230         {
231             closeJarQuietly(jarFile);
232         }
233     }
234 
235     public Object getPluginJarEntry(final String path)
236     {
237         JarFile jarFile = null;
238         try
239         {
240             jarFile = new JarFile(pluginArtifact.toFile());
241             return jarFile.getEntry(path);
242         }
243         catch (final IOException e)
244         {
245             throw new IllegalArgumentException("File must be a jar", e);
246         } finally
247         {
248             closeJarQuietly(jarFile);
249         }
250     }
251 
252     private void closeJarQuietly(final JarFile jarFile)
253     {
254         if (jarFile!=null)
255             try
256         {
257                 jarFile.close();
258         } catch (final IOException e)
259         {
260             // ignore
261         }
262     }
263 }