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          final ZipEntry entry = zip.getEntry("foo");
92          assertNotNull(entry);
93      }
94  
95      public void testTransform() throws Exception
96      {
97          final File file = new PluginJarBuilder()
98                  .addFormattedJava("my.Foo",
99                          "package my;",
100                         "public class Foo {",
101                         "  com.atlassian.plugin.osgi.factory.transform.Fooable bar;",
102                         "}")
103                 .addPluginInformation("foo", "foo", "1.1")
104                 .build();
105 
106         final File copy = transformer.transform(new JarPluginArtifact(file), new ArrayList<HostComponentRegistration>()
107         {
108             {
109                 add(new StubHostComponentRegistration(Fooable.class));
110             }
111         });
112 
113         assertNotNull(copy);
114         assertTrue(copy.getName().contains(String.valueOf(file.lastModified())));
115         assertTrue(copy.getName().endsWith(".jar"));
116         assertEquals(tmpDir.getAbsolutePath(), copy.getParentFile().getParentFile().getAbsolutePath());
117         final JarFile jar = new JarFile(copy);
118         final Attributes attrs = jar.getManifest().getMainAttributes();
119 
120         assertEquals("1.1", attrs.getValue(Constants.BUNDLE_VERSION));
121 
122         assertNotNull(jar.getEntry("META-INF/spring/atlassian-plugins-host-components.xml"));
123     }
124 
125     // TODO: turn this back on PLUG-682
126     public void notTestTransformWithBeanConflictBetweenComponentAndHostComponent() throws Exception
127     {
128         final File file = new PluginJarBuilder()
129                 .addFormattedJava("my.Foo",
130                         "package my;",
131                         "public class Foo {",
132                         "  com.atlassian.plugin.osgi.factory.transform.Fooable bar;",
133                         "}")
134                 .addFormattedResource("atlassian-plugin.xml",
135                         "<atlassian-plugin name='plugin1' key='first' pluginsVersion='2'>",
136                         "    <plugin-info>",
137                         "        <version>1.0</version>",
138                         "    </plugin-info>",
139                         "<component key='host_component1' class='my.Foo'/>",
140                         "</atlassian-plugin>")
141                 .build();
142 
143         try
144         {
145             transformer.transform(new JarPluginArtifact(file), new ArrayList<HostComponentRegistration>()
146             {
147                 {
148                     HostComponentRegistration reg = new StubHostComponentRegistration(Fooable.class);
149                     reg.getProperties().put(PropertyBuilder.BEAN_NAME, "host_component1");
150                     add(reg);
151                 }
152             });
153 
154             fail(PluginTransformationException.class.getSimpleName() + " expected");
155         }
156         catch (PluginTransformationException e)
157         {
158             // good, now check the content inside the error message.
159 
160             // this check looks weird since it relies on message scraping
161             // but without all the information expected here, users would not be able to figure out what went wrong.
162             LOG.info(e.toString());
163             e.getMessage().contains("host_component1");
164             e.getMessage().contains(ComponentSpringStage.BEAN_SOURCE);
165             e.getMessage().contains(HostComponentSpringStage.BEAN_SOURCE);
166         }
167     }
168 
169     // TODO: turn this back on PLUG-682
170     public void notTestTransformWithBeanConflictBetweenComponentAndImportComponent() throws Exception
171     {
172         final File file = new PluginJarBuilder()
173                 .addFormattedJava("my.Foo",
174                         "package my;",
175                         "public class Foo {",
176                         "  com.atlassian.plugin.osgi.factory.transform.Fooable bar;",
177                         "}")
178                 .addFormattedJava("com.atlassian.plugin.osgi.SomeInterface",
179                                   "package com.atlassian.plugin.osgi;",
180                                   "public interface SomeInterface {}")
181                 .addFormattedResource("atlassian-plugin.xml",
182                         "<atlassian-plugin name='plugin1' key='first' pluginsVersion='2'>",
183                         "    <plugin-info>",
184                         "        <version>1.0</version>",
185                         "    </plugin-info>",
186                         "<component key='component1' class='my.Foo'/>",
187                         "<component-import key='component1'>",
188                         "    <interface>com.atlassian.plugin.osgi.SomeInterface</interface>",
189                         "</component-import>",
190                         "</atlassian-plugin>")
191                 .build();
192 
193         try
194         {
195             transformer.transform(new JarPluginArtifact(file), new ArrayList<HostComponentRegistration>());
196             fail(PluginTransformationException.class.getSimpleName() + " expected");
197         }
198         catch (PluginTransformationException e)
199         {
200             // good, now check the content inside the error message.
201 
202             // this check looks weird since it relies on message scraping
203             // but without all the information expected here, users would not be able to figure out what went wrong.
204             LOG.info(e.toString());
205             e.getMessage().contains("component1");
206             e.getMessage().contains(ComponentSpringStage.BEAN_SOURCE);
207             e.getMessage().contains(ComponentImportSpringStage.BEAN_SOURCE);
208         }
209     }
210 
211     public void testImportManifestGenerationOnInterfaces() throws Exception
212     {
213         final File innerJar = new PluginJarBuilder()
214                 .addFormattedJava("my.innerpackage.InnerPackageInterface1",
215                         "package my.innerpackage;",
216                         "public interface InnerPackageInterface1 {}")
217                 .build();
218 
219         final File pluginJar = new PluginJarBuilder()
220                 .addFormattedJava("my.MyFooChild",
221                         "package my;",
222                         "public class MyFooChild extends com.atlassian.plugin.osgi.factory.transform.dummypackage2.DummyClass2 {",
223                         "}")
224                 .addFormattedJava("my2.MyFooInterface",
225                         "package my2;",
226                         "public interface MyFooInterface {}")
227                 .addFormattedResource("atlassian-plugin.xml",
228                         "<atlassian-plugin name='plugin1' key='first' pluginsVersion='2'>",
229                         "    <plugin-info>",
230                         "        <version>1.0</version>",
231                         "    </plugin-info>",
232                         "    <component key='component1' class='my.MyFooChild' public='true'>",
233                         "       <interface>com.atlassian.plugin.osgi.factory.transform.dummypackage0.DummyInterface0</interface>",
234                         "       <interface>com.atlassian.plugin.osgi.factory.transform.dummypackage1.DummyInterface1</interface>",
235                         "       <interface>my.innerpackage.InnerPackageInterface1</interface>",
236                         "       <interface>my2.MyFooInterface</interface>",
237                         "    </component>",
238                         "</atlassian-plugin>")
239                 .addFile("META-INF/lib/mylib.jar", innerJar)
240                 .build();
241 
242         File outputFile = transformer.transform(new JarPluginArtifact(pluginJar), new ArrayList<HostComponentRegistration>());
243 
244         JarFile outputJar = new JarFile(outputFile);
245         String importString = outputJar.getManifest().getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
246 
247         // this should be done by binary scanning.
248         assertTrue(importString.contains("com.atlassian.plugin.osgi.factory.transform.dummypackage2"));
249 
250         // referred to by interface declaration.
251         assertTrue(importString.contains("com.atlassian.plugin.osgi.factory.transform.dummypackage1"));
252 
253         // referred to by interface declaration
254         assertTrue(importString.contains("com.atlassian.plugin.osgi.factory.transform.dummypackage0"));
255 
256         // should not import an interface which exists in plugin itself.
257         assertFalse(importString.contains("my2.MyFooInterface"));
258 
259         // should not import an interface which exists in inner jar.
260         assertFalse(importString.contains("my.innerpackage"));
261     }
262 
263     public void testGenerateCacheName() throws IOException
264     {
265         File tmp = File.createTempFile("asdf", ".jar", tmpDir);
266         assertTrue(DefaultPluginTransformer.generateCacheName(tmp).endsWith(".jar"));
267         tmp = File.createTempFile("asdf", "asdf", tmpDir);
268         assertTrue(DefaultPluginTransformer.generateCacheName(tmp).endsWith(String.valueOf(tmp.lastModified())));
269 
270         tmp = File.createTempFile("asdf", "asdf.", tmpDir);
271         assertTrue(DefaultPluginTransformer.generateCacheName(tmp).endsWith(String.valueOf(tmp.lastModified())));
272 
273         tmp = File.createTempFile("asdf", "asdf.s", tmpDir);
274         assertTrue(DefaultPluginTransformer.generateCacheName(tmp).endsWith(String.valueOf(".s")));
275     }
276 
277     public void testTransformComponentMustNotPerformKeyConversion() throws Exception
278     {
279         File outputJarFile = runTransform();
280 
281         assertBeanNames(outputJarFile, "META-INF/spring/atlassian-plugins-components.xml",
282                         ImmutableMap.<String, String>builder().put("beans", "http://www.springframework.org/schema/beans").build(),
283                         "//beans:bean",
284                         Sets.newHashSet("TESTING1", "testing2"));
285     }
286 
287     public void testTransformImportMustNotPerformKeyConversion() throws Exception
288     {
289         File outputJarFile = runTransform();
290 
291         assertBeanNames(outputJarFile, "META-INF/spring/atlassian-plugins-component-imports.xml",
292                         ImmutableMap.<String, String>builder().put("osgi", "http://www.springframework.org/schema/osgi").build(),
293                         "//osgi:reference",
294                         Sets.newHashSet("TESTING3", "testing4"));
295     }
296 
297     public void testTransformHostComponentMustNotPerformKeyConversion() throws Exception
298     {
299         File outputJarFile = runTransform();
300 
301         assertBeanNames(outputJarFile, "META-INF/spring/atlassian-plugins-host-components.xml",
302                         ImmutableMap.<String, String>builder().put("beans", "http://www.springframework.org/schema/beans").build(),
303                         "//beans:bean",
304                         Sets.newHashSet("TESTING5", "testing6"));
305     }
306 
307     private File runTransform() throws Exception
308     {
309         final File file = new PluginJarBuilder()
310                 .addFormattedJava("my.Foo",
311                         "package my;",
312                         "import com.atlassian.plugin.osgi.factory.transform.Fooable;",
313                         "import com.atlassian.plugin.osgi.factory.transform.FooChild;",
314                         "public class Foo {",
315                         "  private Fooable bar;",
316                         "  public Foo(Fooable bar, FooChild child) { this.bar = bar;} ",
317                         "}")
318                 .addFormattedJava("com.atlassian.plugin.osgi.SomeInterface",
319                                   "package com.atlassian.plugin.osgi;",
320                                   "public interface SomeInterface {}")
321                 .addFormattedResource("atlassian-plugin.xml",
322                         "<atlassian-plugin name='plugin1' key='first' pluginsVersion='2'>",
323                         "    <plugin-info>",
324                         "        <version>1.0</version>",
325                         "    </plugin-info>",
326                         "   <component key='TESTING1' class='my.Foo'/>",
327                         "   <component key='testing2' class='my.Foo'/>",
328                         "   <component-import key='TESTING3'>",
329                         "       <interface>com.atlassian.plugin.osgi.SomeInterface</interface>",
330                         "   </component-import>",
331                         "   <component-import key='testing4'>",
332                         "       <interface>com.atlassian.plugin.osgi.SomeInterface</interface>",
333                         "   </component-import>",
334                         "</atlassian-plugin>")
335                 .build();
336 
337         MockRegistration mockReg1 = new MockRegistration(new Foo(), Fooable.class);
338         mockReg1.getProperties().put(PropertyBuilder.BEAN_NAME, "TESTING5");
339         MockRegistration mockReg2 = new MockRegistration(new FooChild(), FooChild.class);
340         mockReg2.getProperties().put(PropertyBuilder.BEAN_NAME, "testing6");
341 
342         return transformer.transform(new JarPluginArtifact(file), Arrays.<HostComponentRegistration>asList(mockReg1, mockReg2));
343     }
344 
345     private void assertBeanNames(File outputJarFile, String springFileLocation,
346                                  Map<String, String> namespaces, String xpathQuery,
347                                  Set<String> expectedIds) throws Exception
348     {
349         JarFile jarFile = new JarFile(outputJarFile);
350         InputStream inputStream = jarFile.getInputStream(jarFile.getEntry(springFileLocation));
351 
352         SAXReader saxReader = new SAXReader();
353         Document document = saxReader.read(inputStream);
354 
355         XPath xpath = new Dom4jXPath(xpathQuery);
356         xpath.setNamespaceContext(new SimpleNamespaceContext(namespaces));
357         Set<String> foundBeanNames = new HashSet<String>();
358         List<Element> elems = xpath.selectNodes(document);
359         for(Element elem:elems)
360         {
361             foundBeanNames.add(elem.attribute("id").getValue());
362         }
363 
364         assertEquals(expectedIds, foundBeanNames);
365     }
366 }