1   package com.atlassian.plugin.osgi.container.felix;
2   
3   import com.atlassian.plugin.event.impl.DefaultPluginEventManager;
4   import com.atlassian.plugin.osgi.container.impl.DefaultPackageScannerConfiguration;
5   import com.atlassian.plugin.osgi.container.impl.DefaultOsgiPersistentCache;
6   import com.atlassian.plugin.osgi.container.OsgiContainerException;
7   import com.atlassian.plugin.test.PluginJarBuilder;
8   import com.atlassian.plugin.test.PluginTestUtils;
9   import junit.framework.TestCase;
10  import org.apache.commons.io.FileUtils;
11  import org.osgi.framework.Bundle;
12  import org.osgi.framework.BundleException;
13  import org.osgi.framework.Constants;
14  
15  import java.io.File;
16  import java.io.IOException;
17  import java.io.FilenameFilter;
18  import java.net.URISyntaxException;
19  import java.net.URL;
20  
21  public class TestFelixOsgiContainerManager extends TestCase
22  {
23      private File tmpdir;
24      private FelixOsgiContainerManager felix;
25      private URL frameworkBundlesUrl = getClass().getResource("/nothing.zip");
26  
27      @Override
28      public void setUp() throws Exception
29      {
30          super.setUp();
31          tmpdir = PluginTestUtils.createTempDirectory(TestFelixOsgiContainerManager.class);
32          felix = new FelixOsgiContainerManager(frameworkBundlesUrl, new DefaultOsgiPersistentCache(tmpdir), new DefaultPackageScannerConfiguration(),
33                  null, new DefaultPluginEventManager());
34      }
35  
36      @Override
37      public void tearDown() throws Exception
38      {
39          if (felix != null && felix.isRunning())
40          {
41              for (Bundle bundle : felix.getBundles())
42              {
43                  try
44                  {
45                      bundle.uninstall();
46                  }
47                  catch (BundleException ignored) {}
48              }
49          }
50          if (felix != null)
51              felix.stop();
52          felix = null;
53          tmpdir = null;
54          super.tearDown();
55      }
56  
57      public void testDetectXercesOverride()
58      {
59          felix.detectXercesOverride("foo.bar,baz.jim");
60          felix.detectXercesOverride("foo.bar,org.apache.xerces.util;version=\"1.0\",baz.jim");
61          felix.detectXercesOverride("foo.bar,org.apache.xerces.util;version=\"1.0\"");
62  
63          try
64          {
65              felix.detectXercesOverride("foo.bar,org.apache.xerces.util");
66              fail("Should fail validation");
67          }
68          catch (OsgiContainerException ex)
69          {
70              // should fail
71          }
72  
73          try
74          {
75              felix.detectXercesOverride("org.apache.xerces.util");
76              fail("Should fail validation");
77          }
78          catch (OsgiContainerException ex)
79          {
80              // should fail
81          }
82  
83          try
84          {
85              felix.detectXercesOverride("org.apache.xerces.util,bar.baz");
86              fail("Should fail validation");
87          }
88          catch (OsgiContainerException ex)
89          {
90              // should fail
91          }
92  
93      }
94  
95      public void testDeleteDirectory() throws IOException
96      {
97          File dir = new File(tmpdir, "base");
98          dir.mkdir();
99          File subdir = new File(dir, "subdir");
100         subdir.mkdir();
101         File kid = File.createTempFile("foo", "bar", subdir);
102 
103         FileUtils.deleteDirectory(dir);
104         assertTrue(!kid.exists());
105         assertTrue(!subdir.exists());
106         assertTrue(!dir.exists());
107     }
108 
109     public void testStartStop()
110     {
111         FilenameFilter filter = new FilenameFilter()
112             {
113                 public boolean accept(File file, String s)
114                 {
115                     return s.startsWith("felix");
116                 }
117             };
118         int filesNamedFelix = tmpdir.listFiles(filter).length;
119         felix.start();
120         assertTrue(felix.isRunning());
121         assertEquals(1, felix.getBundles().length);
122         felix.stop();
123         assertEquals(filesNamedFelix, tmpdir.listFiles(filter).length);
124     }
125 
126     public void testInstallBundle() throws URISyntaxException
127     {
128         felix.start();
129         assertEquals(1, felix.getBundles().length);
130         File jar = new File(getClass().getResource("/myapp-1.0.jar").toURI());
131         felix.installBundle(jar);
132         assertEquals(2, felix.getBundles().length);
133     }
134 
135     public void testBootDelegation() throws Exception
136     {
137         // Server class extends JUnit TestCase class, which is not available to the bundle
138         File pluginServer = new PluginJarBuilder("plugin")
139             .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
140                 "Bundle-Version: 1.0\n" +
141                 "Bundle-SymbolicName: my.server\n" +
142                 "Bundle-ManifestVersion: 2\n" +
143                 "Export-Package: my.server\n")
144             .addJava("my.server.ServerClass", "package my.server; public class ServerClass extends junit.framework.TestCase {}")
145             .build();
146 
147         // Client is necessary to load the server class in a Felix ContentClassLoader, to avoid the hack in Felix's
148         // R4SearchPolicyCore (approx. line 591) which will use parent delegation if a class cannot be found
149         // and the calling classloader is not a ContentClassLoader.
150         File pluginClient = new PluginJarBuilder("plugin")
151             .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
152                 "Bundle-Version: 1.0\n" +
153                 "Bundle-SymbolicName: my.client\n" +
154                 "Bundle-ManifestVersion: 2\n" +
155                 "Import-Package: my.server\n")
156             .addJava("my.client.ClientClass", "package my.client; public class ClientClass {" +
157                 "public ClientClass() throws ClassNotFoundException {" +
158                     "getClass().getClassLoader().loadClass(\"my.server.ServerClass\");" +
159                 "}}")
160             .build();
161 
162         felix.start();
163         Bundle serverBundle = felix.installBundle(pluginServer);
164         serverBundle.start();
165         Bundle clientBundle = felix.installBundle(pluginClient);
166         clientBundle.start();
167         try
168         {
169             clientBundle.loadClass("my.client.ClientClass").newInstance();
170             fail("Expected exception: NoClassDefFoundError for junit.framework.TestCase");
171         }
172         catch (NoClassDefFoundError expected)
173         {
174         }
175         felix.stop();
176 
177         // This system property exposes the JUnit TestCase class from the parent classloader to the bundle
178         System.setProperty("atlassian.org.osgi.framework.bootdelegation", "junit.framework.*");
179         try
180         {
181             felix.start();
182             serverBundle = felix.installBundle(pluginServer);
183             serverBundle.start();
184             clientBundle = felix.installBundle(pluginClient);
185             clientBundle.start();
186             clientBundle.loadClass("my.client.ClientClass").newInstance();
187             felix.stop();
188         }
189         finally
190         {
191             System.clearProperty("atlassian.org.osgi.framework.bootdelegation");
192         }
193     }
194 
195     public void testInstallBundleTwice() throws URISyntaxException, IOException, BundleException
196     {
197         File plugin = new PluginJarBuilder("plugin")
198                 .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
199                         "Import-Package: javax.swing\n" +
200                         "Bundle-Version: 1.0\n" +
201                         "Bundle-SymbolicName: my.foo.symbolicName\n" +
202                         "Bundle-ManifestVersion: 2\n")
203                 .addResource("foo.txt", "foo")
204                 .build();
205 
206         File pluginUpdate = new PluginJarBuilder("plugin")
207                 .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
208                         "Import-Package: javax.swing\n" +
209                         "Bundle-Version: 1.0\n" +
210                         "Bundle-SymbolicName: my.foo.symbolicName\n" +
211                         "Bundle-ManifestVersion: 2\n")
212                 .addResource("bar.txt", "bar")
213                 .build();
214 
215         felix.start();
216         assertEquals(1, felix.getBundles().length);
217         Bundle bundle = felix.installBundle(plugin);
218         assertEquals(2, felix.getBundles().length);
219         assertEquals("my.foo.symbolicName", bundle.getSymbolicName());
220         assertEquals("1.0", bundle.getHeaders().get(Constants.BUNDLE_VERSION));
221         assertEquals(Bundle.INSTALLED, bundle.getState());
222         assertNotNull(bundle.getResource("foo.txt"));
223         assertNull(bundle.getResource("bar.txt"));
224         bundle.start();
225         assertEquals(Bundle.ACTIVE, bundle.getState());
226         Bundle bundleUpdate = felix.installBundle(pluginUpdate);
227         assertEquals(2, felix.getBundles().length);
228         assertEquals(Bundle.INSTALLED, bundleUpdate.getState());
229         bundleUpdate.start();
230         assertEquals(Bundle.ACTIVE, bundleUpdate.getState());
231         assertNull(bundleUpdate.getResource("foo.txt"));
232         assertNotNull(bundleUpdate.getResource("bar.txt"));
233     }
234 
235     public void testInstallBundleTwiceDifferentSymbolicNames() throws URISyntaxException, IOException, BundleException
236     {
237         File plugin = new PluginJarBuilder("plugin")
238                 .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
239                         "Import-Package: javax.swing\n" +
240                         "Bundle-Version: 1.0\n" +
241                         "Bundle-SymbolicName: my.foo\n" +
242                         "Atlassian-Plugin-Key: my.foo.symbolicName\n" +
243                         "Bundle-ManifestVersion: 2\n")
244                 .addResource("foo.txt", "foo")
245                 .build();
246 
247         File pluginUpdate = new PluginJarBuilder("plugin")
248                 .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
249                         "Import-Package: javax.swing\n" +
250                         "Bundle-Version: 1.0\n" +
251                         "Atlassian-Plugin-Key: my.foo.symbolicName\n" +
252                         "Bundle-SymbolicName: my.bar\n" +
253                         "Bundle-ManifestVersion: 2\n")
254                 .addResource("bar.txt", "bar")
255                 .build();
256 
257         felix.start();
258         assertEquals(1, felix.getBundles().length);
259         Bundle bundle = felix.installBundle(plugin);
260         assertEquals(2, felix.getBundles().length);
261         assertEquals("1.0", bundle.getHeaders().get(Constants.BUNDLE_VERSION));
262         assertEquals(Bundle.INSTALLED, bundle.getState());
263         assertNotNull(bundle.getResource("foo.txt"));
264         assertNull(bundle.getResource("bar.txt"));
265         bundle.start();
266         assertEquals(Bundle.ACTIVE, bundle.getState());
267         Bundle bundleUpdate = felix.installBundle(pluginUpdate);
268         assertEquals(2, felix.getBundles().length);
269         assertEquals(Bundle.INSTALLED, bundleUpdate.getState());
270         bundleUpdate.start();
271         assertEquals(Bundle.ACTIVE, bundleUpdate.getState());
272         assertNull(bundleUpdate.getResource("foo.txt"));
273         assertNotNull(bundleUpdate.getResource("bar.txt"));
274     }
275 
276     public void testInstallFailure() throws Exception
277     {
278         File plugin = new PluginJarBuilder("plugin")
279                 .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
280                         "Bundle-Version: 1.0\n" +
281                         "Import-Package: foo.missing.package\n" +
282                         "Bundle-SymbolicName: my.foo.symbolicName\n" +
283                         "Bundle-ManifestVersion: 2\n" )
284                 .build();
285         felix.start();
286 
287         Bundle bundle = felix.installBundle(plugin);
288         try {
289             bundle.loadClass("foo.bar");
290             fail("Should have thrown exception");
291         } catch (ClassNotFoundException ex) {
292             // no worries
293         }
294     }
295 }