View Javadoc

1   package com.atlassian.plugin.osgi.factory.transform;
2   
3   import com.atlassian.plugin.PluginAccessor;
4   import com.atlassian.plugin.JarPluginArtifact;
5   import com.atlassian.plugin.osgi.factory.transform.stage.ComponentImportSpringStage;
6   import com.atlassian.plugin.osgi.factory.transform.stage.ComponentSpringStage;
7   import com.atlassian.plugin.osgi.factory.transform.stage.HostComponentSpringStage;
8   import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
9   import com.atlassian.plugin.osgi.container.impl.DefaultOsgiPersistentCache;
10  import com.atlassian.plugin.osgi.container.OsgiContainerManager;
11  import com.atlassian.plugin.osgi.factory.transform.model.SystemExports;
12  import com.atlassian.plugin.osgi.hostcomponents.PropertyBuilder;
13  import com.atlassian.plugin.osgi.hostcomponents.impl.MockRegistration;
14  import com.atlassian.plugin.test.PluginJarBuilder;
15  import com.atlassian.plugin.test.PluginTestUtils;
16  
17  import com.google.common.collect.ImmutableMap;
18  import com.google.common.collect.Sets;
19  import org.dom4j.Document;
20  import org.dom4j.Element;
21  import org.dom4j.io.SAXReader;
22  import org.jaxen.SimpleNamespaceContext;
23  import org.jaxen.XPath;
24  import org.jaxen.dom4j.Dom4jXPath;
25  import org.osgi.framework.Constants;
26  import org.osgi.framework.ServiceReference;
27  import static org.mockito.Mockito.mock;
28  import static org.mockito.Mockito.when;
29  
30  import java.io.File;
31  import java.io.IOException;
32  import java.io.InputStream;
33  import java.net.URISyntaxException;
34  import java.util.ArrayList;
35  import java.util.Arrays;
36  import java.util.HashMap;
37  import java.util.HashSet;
38  import java.util.List;
39  import java.util.Map;
40  import java.util.Set;
41  import java.util.jar.Attributes;
42  import java.util.jar.JarFile;
43  import java.util.zip.ZipEntry;
44  import java.util.zip.ZipFile;
45  
46  import junit.framework.TestCase;
47  import org.slf4j.Logger;
48  import org.slf4j.LoggerFactory;
49  
50  public class TestDefaultPluginTransformer extends TestCase
51  {
52      private static final Logger LOG = LoggerFactory.getLogger(TestDefaultPluginTransformer.class);
53  
54      private DefaultPluginTransformer transformer;
55      private File tmpDir;
56  
57      @Override
58      protected void setUp() throws Exception
59      {
60          super.setUp();
61          OsgiContainerManager osgiContainerManager = mock(OsgiContainerManager.class);
62          when(osgiContainerManager.getRegisteredServices()).thenReturn(new ServiceReference[0]);
63          tmpDir = PluginTestUtils.createTempDirectory("plugin-transformer");
64          transformer = new DefaultPluginTransformer(new DefaultOsgiPersistentCache(tmpDir), SystemExports.NONE, null, PluginAccessor.Descriptor.FILENAME, osgiContainerManager);
65      }
66  
67      @Override
68      protected void tearDown() throws Exception
69      {
70          super.tearDown();
71          tmpDir = null;
72          transformer = null;
73      }
74  
75      public void testAddFilesToZip() throws URISyntaxException, IOException
76      {
77          final File file = PluginTestUtils.getFileForResource("myapp-1.0-plugin.jar");
78  
79          final Map<String, byte[]> files = new HashMap<String, byte[]>()
80          {
81              {
82                  put("foo", "bar".getBytes());
83              }
84          };
85          final File copy = transformer.addFilesToExistingZip(file, files);
86          assertNotNull(copy);
87          assertTrue(!copy.getName().equals(file.getName()));
88          assertTrue(copy.length() != file.length());
89  
90          final ZipFile zip = new ZipFile(copy);
91          try
92          {
93              final ZipEntry entry = zip.getEntry("foo");
94              assertNotNull(entry);
95          }
96          finally
97          {
98              closeQuietly(zip);
99          }
100     }
101 
102     public void testTransform() throws Exception
103     {
104         final File file = new PluginJarBuilder()
105                 .addFormattedJava("my.Foo",
106                         "package my;",
107                         "public class Foo {",
108                         "  com.atlassian.plugin.osgi.factory.transform.Fooable bar;",
109                         "}")
110                 .addPluginInformation("foo", "foo", "1.1")
111                 .build();
112 
113         final File copy = transformer.transform(new JarPluginArtifact(file), new ArrayList<HostComponentRegistration>()
114         {
115             {
116                 add(new StubHostComponentRegistration(Fooable.class));
117             }
118         });
119 
120         assertNotNull(copy);
121         assertTrue(copy.getName().contains(String.valueOf(file.lastModified())));
122         assertTrue(copy.getName().endsWith(".jar"));
123         assertEquals(tmpDir.getAbsolutePath(), copy.getParentFile().getParentFile().getAbsolutePath());
124         final JarFile jar = new JarFile(copy);
125         try
126         {
127             final Attributes attrs = jar.getManifest().getMainAttributes();
128             assertEquals("1.1", attrs.getValue(Constants.BUNDLE_VERSION));
129             assertNotNull(jar.getEntry("META-INF/spring/atlassian-plugins-host-components.xml"));
130         }
131         finally
132         {
133             closeQuietly(jar);
134         }
135     }
136 
137     // TODO: turn this back on PLUG-682
138     public void notTestTransformWithBeanConflictBetweenComponentAndHostComponent() throws Exception
139     {
140         final File file = new PluginJarBuilder()
141                 .addFormattedJava("my.Foo",
142                         "package my;",
143                         "public class Foo {",
144                         "  com.atlassian.plugin.osgi.factory.transform.Fooable bar;",
145                         "}")
146                 .addFormattedResource("atlassian-plugin.xml",
147                         "<atlassian-plugin name='plugin1' key='first' pluginsVersion='2'>",
148                         "    <plugin-info>",
149                         "        <version>1.0</version>",
150                         "    </plugin-info>",
151                         "<component key='host_component1' class='my.Foo'/>",
152                         "</atlassian-plugin>")
153                 .build();
154 
155         try
156         {
157             transformer.transform(new JarPluginArtifact(file), new ArrayList<HostComponentRegistration>()
158             {
159                 {
160                     HostComponentRegistration reg = new StubHostComponentRegistration(Fooable.class);
161                     reg.getProperties().put(PropertyBuilder.BEAN_NAME, "host_component1");
162                     add(reg);
163                 }
164             });
165 
166             fail(PluginTransformationException.class.getSimpleName() + " expected");
167         }
168         catch (PluginTransformationException e)
169         {
170             // good, now check the content inside the error message.
171 
172             // this check looks weird since it relies on message scraping
173             // but without all the information expected here, users would not be able to figure out what went wrong.
174             LOG.info(e.toString());
175             e.getMessage().contains("host_component1");
176             e.getMessage().contains(ComponentSpringStage.BEAN_SOURCE);
177             e.getMessage().contains(HostComponentSpringStage.BEAN_SOURCE);
178         }
179     }
180 
181     // TODO: turn this back on PLUG-682
182     public void notTestTransformWithBeanConflictBetweenComponentAndImportComponent() throws Exception
183     {
184         final File file = new PluginJarBuilder()
185                 .addFormattedJava("my.Foo",
186                         "package my;",
187                         "public class Foo {",
188                         "  com.atlassian.plugin.osgi.factory.transform.Fooable bar;",
189                         "}")
190                 .addFormattedJava("com.atlassian.plugin.osgi.SomeInterface",
191                                   "package com.atlassian.plugin.osgi;",
192                                   "public interface SomeInterface {}")
193                 .addFormattedResource("atlassian-plugin.xml",
194                         "<atlassian-plugin name='plugin1' key='first' pluginsVersion='2'>",
195                         "    <plugin-info>",
196                         "        <version>1.0</version>",
197                         "    </plugin-info>",
198                         "<component key='component1' class='my.Foo'/>",
199                         "<component-import key='component1'>",
200                         "    <interface>com.atlassian.plugin.osgi.SomeInterface</interface>",
201                         "</component-import>",
202                         "</atlassian-plugin>")
203                 .build();
204 
205         try
206         {
207             transformer.transform(new JarPluginArtifact(file), new ArrayList<HostComponentRegistration>());
208             fail(PluginTransformationException.class.getSimpleName() + " expected");
209         }
210         catch (PluginTransformationException e)
211         {
212             // good, now check the content inside the error message.
213 
214             // this check looks weird since it relies on message scraping
215             // but without all the information expected here, users would not be able to figure out what went wrong.
216             LOG.info(e.toString());
217             e.getMessage().contains("component1");
218             e.getMessage().contains(ComponentSpringStage.BEAN_SOURCE);
219             e.getMessage().contains(ComponentImportSpringStage.BEAN_SOURCE);
220         }
221     }
222 
223     public void testImportManifestGenerationOnInterfaces() throws Exception
224     {
225         final File innerJar = new PluginJarBuilder()
226                 .addFormattedJava("my.innerpackage.InnerPackageInterface1",
227                         "package my.innerpackage;",
228                         "public interface InnerPackageInterface1 {}")
229                 .build();
230 
231         final File pluginJar = new PluginJarBuilder()
232                 .addFormattedJava("my.MyFooChild",
233                         "package my;",
234                         "public class MyFooChild extends com.atlassian.plugin.osgi.factory.transform.dummypackage2.DummyClass2 {",
235                         "}")
236                 .addFormattedJava("my2.MyFooInterface",
237                         "package my2;",
238                         "public interface MyFooInterface {}")
239                 .addFormattedResource("atlassian-plugin.xml",
240                         "<atlassian-plugin name='plugin1' key='first' pluginsVersion='2'>",
241                         "    <plugin-info>",
242                         "        <version>1.0</version>",
243                         "    </plugin-info>",
244                         "    <component key='component1' class='my.MyFooChild' public='true'>",
245                         "       <interface>com.atlassian.plugin.osgi.factory.transform.dummypackage0.DummyInterface0</interface>",
246                         "       <interface>com.atlassian.plugin.osgi.factory.transform.dummypackage1.DummyInterface1</interface>",
247                         "       <interface>my.innerpackage.InnerPackageInterface1</interface>",
248                         "       <interface>my2.MyFooInterface</interface>",
249                         "    </component>",
250                         "</atlassian-plugin>")
251                 .addFile("META-INF/lib/mylib.jar", innerJar)
252                 .build();
253 
254         File outputFile = transformer.transform(new JarPluginArtifact(pluginJar), new ArrayList<HostComponentRegistration>());
255 
256         JarFile outputJar = new JarFile(outputFile);
257         String importString;
258         try
259         {
260             importString = outputJar.getManifest().getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
261         }
262         finally
263         {
264             closeQuietly(outputJar);
265         }
266 
267         // this should be done by binary scanning.
268         assertTrue(importString.contains("com.atlassian.plugin.osgi.factory.transform.dummypackage2"));
269 
270         // referred to by interface declaration.
271         assertTrue(importString.contains("com.atlassian.plugin.osgi.factory.transform.dummypackage1"));
272 
273         // referred to by interface declaration
274         assertTrue(importString.contains("com.atlassian.plugin.osgi.factory.transform.dummypackage0"));
275 
276         // should not import an interface which exists in plugin itself.
277         assertFalse(importString.contains("my2.MyFooInterface"));
278 
279         // should not import an interface which exists in inner jar.
280         assertFalse(importString.contains("my.innerpackage"));
281     }
282 
283     public void testGenerateCacheName() throws IOException
284     {
285         File tmp = File.createTempFile("asdf", ".jar", tmpDir);
286         assertTrue(DefaultPluginTransformer.generateCacheName(tmp).endsWith(".jar"));
287         tmp = File.createTempFile("asdf", "asdf", tmpDir);
288         assertTrue(DefaultPluginTransformer.generateCacheName(tmp).endsWith(String.valueOf(tmp.lastModified())));
289 
290         tmp = File.createTempFile("asdf", "asdf.", tmpDir);
291         assertTrue(DefaultPluginTransformer.generateCacheName(tmp).endsWith(String.valueOf(tmp.lastModified())));
292 
293         tmp = File.createTempFile("asdf", "asdf.s", tmpDir);
294         assertTrue(DefaultPluginTransformer.generateCacheName(tmp).endsWith(String.valueOf(".s")));
295     }
296 
297     public void testTransformComponentMustNotPerformKeyConversion() throws Exception
298     {
299         File outputJarFile = runTransform();
300 
301         assertBeanNames(outputJarFile, "META-INF/spring/atlassian-plugins-components.xml",
302                         ImmutableMap.<String, String>builder().put("beans", "http://www.springframework.org/schema/beans").build(),
303                         "//beans:bean",
304                         Sets.newHashSet("TESTING1", "testing2"));
305     }
306 
307     public void testTransformImportMustNotPerformKeyConversion() throws Exception
308     {
309         File outputJarFile = runTransform();
310 
311         assertBeanNames(outputJarFile, "META-INF/spring/atlassian-plugins-component-imports.xml",
312                         ImmutableMap.<String, String>builder().put("osgi", "http://www.eclipse.org/gemini/blueprint/schema/blueprint").build(),
313                         "//osgi:reference",
314                         Sets.newHashSet("TESTING3", "testing4"));
315     }
316 
317     public void testTransformHostComponentMustNotPerformKeyConversion() throws Exception
318     {
319         File outputJarFile = runTransform();
320 
321         assertBeanNames(outputJarFile, "META-INF/spring/atlassian-plugins-host-components.xml",
322                         ImmutableMap.<String, String>builder().put("beans", "http://www.springframework.org/schema/beans").build(),
323                         "//beans:bean",
324                         Sets.newHashSet("TESTING5", "testing6"));
325     }
326 
327     private File runTransform() throws Exception
328     {
329         final File file = new PluginJarBuilder()
330                 .addFormattedJava("my.Foo",
331                         "package my;",
332                         "import com.atlassian.plugin.osgi.factory.transform.Fooable;",
333                         "import com.atlassian.plugin.osgi.factory.transform.FooChild;",
334                         "public class Foo {",
335                         "  private Fooable bar;",
336                         "  public Foo(Fooable bar, FooChild child) { this.bar = bar;} ",
337                         "}")
338                 .addFormattedJava("com.atlassian.plugin.osgi.SomeInterface",
339                                   "package com.atlassian.plugin.osgi;",
340                                   "public interface SomeInterface {}")
341                 .addFormattedResource("atlassian-plugin.xml",
342                         "<atlassian-plugin name='plugin1' key='first' pluginsVersion='2'>",
343                         "    <plugin-info>",
344                         "        <version>1.0</version>",
345                         "    </plugin-info>",
346                         "   <component key='TESTING1' class='my.Foo'/>",
347                         "   <component key='testing2' class='my.Foo'/>",
348                         "   <component-import key='TESTING3'>",
349                         "       <interface>com.atlassian.plugin.osgi.SomeInterface</interface>",
350                         "   </component-import>",
351                         "   <component-import key='testing4'>",
352                         "       <interface>com.atlassian.plugin.osgi.SomeInterface</interface>",
353                         "   </component-import>",
354                         "</atlassian-plugin>")
355                 .build();
356 
357         MockRegistration mockReg1 = new MockRegistration(new Foo(), Fooable.class);
358         mockReg1.getProperties().put(PropertyBuilder.BEAN_NAME, "TESTING5");
359         MockRegistration mockReg2 = new MockRegistration(new FooChild(), FooChild.class);
360         mockReg2.getProperties().put(PropertyBuilder.BEAN_NAME, "testing6");
361 
362         return transformer.transform(new JarPluginArtifact(file), Arrays.<HostComponentRegistration>asList(mockReg1, mockReg2));
363     }
364 
365     private void assertBeanNames(File outputJarFile, String springFileLocation,
366                                  Map<String, String> namespaces, String xpathQuery,
367                                  Set<String> expectedIds) throws Exception
368     {
369         JarFile jarFile = new JarFile(outputJarFile);
370         Set<String> foundBeanNames = new HashSet<String>();
371         try
372         {
373             InputStream inputStream = jarFile.getInputStream(jarFile.getEntry(springFileLocation));
374 
375             SAXReader saxReader = new SAXReader();
376             Document document = saxReader.read(inputStream);
377 
378             XPath xpath = new Dom4jXPath(xpathQuery);
379             xpath.setNamespaceContext(new SimpleNamespaceContext(namespaces));
380             List<Element> elems = xpath.selectNodes(document);
381             for(Element elem : elems)
382             {
383                 foundBeanNames.add(elem.attribute("id").getValue());
384             }
385         }
386         finally
387         {
388             closeQuietly(jarFile);
389         }
390 
391         assertEquals(expectedIds, foundBeanNames);
392     }
393 
394     /** TODO remove once we're firmly on Java 7, when ZipFile becomes Closeable. */
395     private static void closeQuietly(ZipFile zipFile)
396     {
397         try
398         {
399             zipFile.close();
400         }
401         catch (IOException e)
402         {
403             LOG.debug("Error closing zipFile: {}", zipFile.getName(), e);
404         }
405     }
406 }