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.JarEntry;
43 import java.util.jar.JarFile;
44 import java.util.zip.Deflater;
45 import java.util.zip.ZipEntry;
46 import java.util.zip.ZipFile;
47
48 import junit.framework.TestCase;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 public class TestDefaultPluginTransformer extends TestCase
53 {
54 private static final Logger LOG = LoggerFactory.getLogger(TestDefaultPluginTransformer.class);
55
56 private DefaultPluginTransformer transformer;
57 private File tmpDir;
58
59 @Override
60 protected void setUp() throws Exception
61 {
62 super.setUp();
63 OsgiContainerManager osgiContainerManager = mock(OsgiContainerManager.class);
64 when(osgiContainerManager.getRegisteredServices()).thenReturn(new ServiceReference[0]);
65 tmpDir = PluginTestUtils.createTempDirectory("plugin-transformer");
66 transformer = new DefaultPluginTransformer(new DefaultOsgiPersistentCache(tmpDir), SystemExports.NONE, null, PluginAccessor.Descriptor.FILENAME, osgiContainerManager);
67 }
68
69 @Override
70 protected void tearDown() throws Exception
71 {
72 super.tearDown();
73 tmpDir = null;
74 transformer = null;
75 }
76
77 public void testAddFilesToZip() throws URISyntaxException, IOException
78 {
79 final File file = PluginTestUtils.getFileForResource("myapp-1.0-plugin.jar");
80
81 final Map<String, byte[]> files = new HashMap<String, byte[]>()
82 {
83 {
84 put("foo", "bar".getBytes());
85 }
86 };
87 final File copy = transformer.addFilesToExistingZip(file, files);
88 assertNotNull(copy);
89 assertTrue(!copy.getName().equals(file.getName()));
90 assertTrue(copy.length() != file.length());
91
92 final ZipFile zip = new ZipFile(copy);
93 try
94 {
95 final ZipEntry entry = zip.getEntry("foo");
96 assertNotNull(entry);
97 }
98 finally
99 {
100 closeQuietly(zip);
101 }
102 }
103
104 public void testTransform() throws Exception
105 {
106 final File file = new PluginJarBuilder()
107 .addFormattedJava("my.Foo",
108 "package my;",
109 "public class Foo {",
110 " com.atlassian.plugin.osgi.factory.transform.Fooable bar;",
111 "}")
112 .addPluginInformation("foo", "foo", "1.1")
113 .build();
114
115 final File copy = transformer.transform(new JarPluginArtifact(file), new ArrayList<HostComponentRegistration>()
116 {
117 {
118 add(new StubHostComponentRegistration(Fooable.class));
119 }
120 });
121
122 assertNotNull(copy);
123 assertTrue(copy.getName().contains(String.valueOf(file.lastModified())));
124 assertTrue(copy.getName().endsWith(".jar"));
125 assertEquals(tmpDir.getAbsolutePath(), copy.getParentFile().getParentFile().getAbsolutePath());
126 final JarFile jar = new JarFile(copy);
127 try
128 {
129 final Attributes attrs = jar.getManifest().getMainAttributes();
130 assertEquals("1.1", attrs.getValue(Constants.BUNDLE_VERSION));
131 assertNotNull(jar.getEntry("META-INF/spring/atlassian-plugins-host-components.xml"));
132 }
133 finally
134 {
135 closeQuietly(jar);
136 }
137 }
138
139
140 public void notTestTransformWithBeanConflictBetweenComponentAndHostComponent() throws Exception
141 {
142 final File file = new PluginJarBuilder()
143 .addFormattedJava("my.Foo",
144 "package my;",
145 "public class Foo {",
146 " com.atlassian.plugin.osgi.factory.transform.Fooable bar;",
147 "}")
148 .addFormattedResource("atlassian-plugin.xml",
149 "<atlassian-plugin name='plugin1' key='first' pluginsVersion='2'>",
150 " <plugin-info>",
151 " <version>1.0</version>",
152 " </plugin-info>",
153 "<component key='host_component1' class='my.Foo'/>",
154 "</atlassian-plugin>")
155 .build();
156
157 try
158 {
159 transformer.transform(new JarPluginArtifact(file), new ArrayList<HostComponentRegistration>()
160 {
161 {
162 HostComponentRegistration reg = new StubHostComponentRegistration(Fooable.class);
163 reg.getProperties().put(PropertyBuilder.BEAN_NAME, "host_component1");
164 add(reg);
165 }
166 });
167
168 fail(PluginTransformationException.class.getSimpleName() + " expected");
169 }
170 catch (PluginTransformationException e)
171 {
172
173
174
175
176 LOG.info(e.toString());
177 e.getMessage().contains("host_component1");
178 e.getMessage().contains(ComponentSpringStage.BEAN_SOURCE);
179 e.getMessage().contains(HostComponentSpringStage.BEAN_SOURCE);
180 }
181 }
182
183
184 public void notTestTransformWithBeanConflictBetweenComponentAndImportComponent() throws Exception
185 {
186 final File file = new PluginJarBuilder()
187 .addFormattedJava("my.Foo",
188 "package my;",
189 "public class Foo {",
190 " com.atlassian.plugin.osgi.factory.transform.Fooable bar;",
191 "}")
192 .addFormattedJava("com.atlassian.plugin.osgi.SomeInterface",
193 "package com.atlassian.plugin.osgi;",
194 "public interface SomeInterface {}")
195 .addFormattedResource("atlassian-plugin.xml",
196 "<atlassian-plugin name='plugin1' key='first' pluginsVersion='2'>",
197 " <plugin-info>",
198 " <version>1.0</version>",
199 " </plugin-info>",
200 "<component key='component1' class='my.Foo'/>",
201 "<component-import key='component1'>",
202 " <interface>com.atlassian.plugin.osgi.SomeInterface</interface>",
203 "</component-import>",
204 "</atlassian-plugin>")
205 .build();
206
207 try
208 {
209 transformer.transform(new JarPluginArtifact(file), new ArrayList<HostComponentRegistration>());
210 fail(PluginTransformationException.class.getSimpleName() + " expected");
211 }
212 catch (PluginTransformationException e)
213 {
214
215
216
217
218 LOG.info(e.toString());
219 e.getMessage().contains("component1");
220 e.getMessage().contains(ComponentSpringStage.BEAN_SOURCE);
221 e.getMessage().contains(ComponentImportSpringStage.BEAN_SOURCE);
222 }
223 }
224
225 public void testImportManifestGenerationOnInterfaces() throws Exception
226 {
227 final File innerJar = new PluginJarBuilder()
228 .addFormattedJava("my.innerpackage.InnerPackageInterface1",
229 "package my.innerpackage;",
230 "public interface InnerPackageInterface1 {}")
231 .build();
232
233 final File pluginJar = new PluginJarBuilder()
234 .addFormattedJava("my.MyFooChild",
235 "package my;",
236 "public class MyFooChild extends com.atlassian.plugin.osgi.factory.transform.dummypackage2.DummyClass2 {",
237 "}")
238 .addFormattedJava("my2.MyFooInterface",
239 "package my2;",
240 "public interface MyFooInterface {}")
241 .addFormattedResource("atlassian-plugin.xml",
242 "<atlassian-plugin name='plugin1' key='first' pluginsVersion='2'>",
243 " <plugin-info>",
244 " <version>1.0</version>",
245 " </plugin-info>",
246 " <component key='component1' class='my.MyFooChild' public='true'>",
247 " <interface>com.atlassian.plugin.osgi.factory.transform.dummypackage0.DummyInterface0</interface>",
248 " <interface>com.atlassian.plugin.osgi.factory.transform.dummypackage1.DummyInterface1</interface>",
249 " <interface>my.innerpackage.InnerPackageInterface1</interface>",
250 " <interface>my2.MyFooInterface</interface>",
251 " </component>",
252 "</atlassian-plugin>")
253 .addFile("META-INF/lib/mylib.jar", innerJar)
254 .build();
255
256 File outputFile = transformer.transform(new JarPluginArtifact(pluginJar), new ArrayList<HostComponentRegistration>());
257
258 JarFile outputJar = new JarFile(outputFile);
259 String importString;
260 try
261 {
262 importString = outputJar.getManifest().getMainAttributes().getValue(Constants.IMPORT_PACKAGE);
263 }
264 finally
265 {
266 closeQuietly(outputJar);
267 }
268
269
270 assertTrue(importString.contains("com.atlassian.plugin.osgi.factory.transform.dummypackage2"));
271
272
273 assertTrue(importString.contains("com.atlassian.plugin.osgi.factory.transform.dummypackage1"));
274
275
276 assertTrue(importString.contains("com.atlassian.plugin.osgi.factory.transform.dummypackage0"));
277
278
279 assertFalse(importString.contains("my2.MyFooInterface"));
280
281
282 assertFalse(importString.contains("my.innerpackage"));
283 }
284
285 public void testGenerateCacheName() throws IOException
286 {
287 File tmp = File.createTempFile("asdf", ".jar", tmpDir);
288 assertTrue(DefaultPluginTransformer.generateCacheName(tmp).endsWith(".jar"));
289 tmp = File.createTempFile("asdf", "asdf", tmpDir);
290 assertTrue(DefaultPluginTransformer.generateCacheName(tmp).endsWith(String.valueOf(tmp.lastModified())));
291
292 tmp = File.createTempFile("asdf", "asdf.", tmpDir);
293 assertTrue(DefaultPluginTransformer.generateCacheName(tmp).endsWith(String.valueOf(tmp.lastModified())));
294
295 tmp = File.createTempFile("asdf", "asdf.s", tmpDir);
296 assertTrue(DefaultPluginTransformer.generateCacheName(tmp).endsWith(String.valueOf(".s")));
297 }
298
299 public void testTransformComponentMustNotPerformKeyConversion() throws Exception
300 {
301 File outputJarFile = runTransform();
302
303 assertBeanNames(outputJarFile, "META-INF/spring/atlassian-plugins-components.xml",
304 ImmutableMap.<String, String>builder().put("beans", "http://www.springframework.org/schema/beans").build(),
305 "//beans:bean",
306 Sets.newHashSet("TESTING1", "testing2"));
307 }
308
309 public void testTransformImportMustNotPerformKeyConversion() throws Exception
310 {
311 File outputJarFile = runTransform();
312
313 assertBeanNames(outputJarFile, "META-INF/spring/atlassian-plugins-component-imports.xml",
314 ImmutableMap.<String, String>builder().put("osgi", "http://www.springframework.org/schema/osgi").build(),
315 "//osgi:reference",
316 Sets.newHashSet("TESTING3", "testing4"));
317 }
318
319 public void testTransformHostComponentMustNotPerformKeyConversion() throws Exception
320 {
321 File outputJarFile = runTransform();
322
323 assertBeanNames(outputJarFile, "META-INF/spring/atlassian-plugins-host-components.xml",
324 ImmutableMap.<String, String>builder().put("beans", "http://www.springframework.org/schema/beans").build(),
325 "//beans:bean",
326 Sets.newHashSet("TESTING5", "testing6"));
327 }
328
329 public void testDefaultTransformedJarIsNotCompressed() throws Exception
330 {
331
332
333 final File transformedFile = runTransform();
334 for(final JarEntry jarEntry : JarUtils.getEntries(transformedFile))
335 {
336
337
338
339 assertTrue(jarEntry.getCompressedSize() >= jarEntry.getSize());
340 }
341 }
342
343 public void testBestSpeedCompressionYieldsCompressedTransformedJar() throws Exception
344 {
345 compressionOptionYieldsCompressedTransformedJar(Deflater.BEST_SPEED);
346 }
347
348 public void testBestSizeCompressionYieldsCompressedTransformedJar() throws Exception
349 {
350 compressionOptionYieldsCompressedTransformedJar(Deflater.BEST_COMPRESSION);
351 }
352
353 public void compressionOptionYieldsCompressedTransformedJar(final int level) throws Exception
354 {
355
356
357
358 System.setProperty(DefaultPluginTransformer.TRANSFORM_COMPRESSION_LEVEL, Integer.toString(level));
359
360 final JarEntry jarEntry = JarUtils.getEntry(runTransform(), PluginAccessor.Descriptor.FILENAME);
361
362
363 assertTrue(jarEntry.getCompressedSize() < jarEntry.getSize());
364
365 System.clearProperty(DefaultPluginTransformer.TRANSFORM_COMPRESSION_LEVEL);
366 }
367
368 private File runTransform() throws Exception
369 {
370 final File file = new PluginJarBuilder()
371 .addFormattedJava("my.Foo",
372 "package my;",
373 "import com.atlassian.plugin.osgi.factory.transform.Fooable;",
374 "import com.atlassian.plugin.osgi.factory.transform.FooChild;",
375 "public class Foo {",
376 " private Fooable bar;",
377 " public Foo(Fooable bar, FooChild child) { this.bar = bar;} ",
378 "}")
379 .addFormattedJava("com.atlassian.plugin.osgi.SomeInterface",
380 "package com.atlassian.plugin.osgi;",
381 "public interface SomeInterface {}")
382 .addFormattedResource("atlassian-plugin.xml",
383 "<atlassian-plugin name='plugin1' key='first' pluginsVersion='2'>",
384 " <plugin-info>",
385 " <version>1.0</version>",
386 " </plugin-info>",
387 " <component key='TESTING1' class='my.Foo'/>",
388 " <component key='testing2' class='my.Foo'/>",
389 " <component-import key='TESTING3'>",
390 " <interface>com.atlassian.plugin.osgi.SomeInterface</interface>",
391 " </component-import>",
392 " <component-import key='testing4'>",
393 " <interface>com.atlassian.plugin.osgi.SomeInterface</interface>",
394 " </component-import>",
395 "</atlassian-plugin>")
396 .build();
397
398 MockRegistration mockReg1 = new MockRegistration(new Foo(), Fooable.class);
399 mockReg1.getProperties().put(PropertyBuilder.BEAN_NAME, "TESTING5");
400 MockRegistration mockReg2 = new MockRegistration(new FooChild(), FooChild.class);
401 mockReg2.getProperties().put(PropertyBuilder.BEAN_NAME, "testing6");
402
403 return transformer.transform(new JarPluginArtifact(file), Arrays.<HostComponentRegistration>asList(mockReg1, mockReg2));
404 }
405
406 private void assertBeanNames(File outputJarFile, String springFileLocation,
407 Map<String, String> namespaces, String xpathQuery,
408 Set<String> expectedIds) throws Exception
409 {
410 JarFile jarFile = new JarFile(outputJarFile);
411 Set<String> foundBeanNames = new HashSet<String>();
412 try
413 {
414 InputStream inputStream = jarFile.getInputStream(jarFile.getEntry(springFileLocation));
415
416 SAXReader saxReader = new SAXReader();
417 Document document = saxReader.read(inputStream);
418
419 XPath xpath = new Dom4jXPath(xpathQuery);
420 xpath.setNamespaceContext(new SimpleNamespaceContext(namespaces));
421 List<Element> elems = xpath.selectNodes(document);
422 for(Element elem : elems)
423 {
424 foundBeanNames.add(elem.attribute("id").getValue());
425 }
426 }
427 finally
428 {
429 closeQuietly(jarFile);
430 }
431
432 assertEquals(expectedIds, foundBeanNames);
433 }
434
435
436 private static void closeQuietly(ZipFile zipFile)
437 {
438 try
439 {
440 zipFile.close();
441 }
442 catch (IOException e)
443 {
444 LOG.debug("Error closing zipFile: {}", zipFile.getName(), e);
445 }
446 }
447 }