blob: 6a9a85c8dd4975470c227754c12cfd6b3a4996f6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.brooklyn.core.mgmt.osgi;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarInputStream;
import org.apache.brooklyn.api.entity.Entity;
import org.apache.brooklyn.test.support.TestResourceUnavailableException;
import org.apache.brooklyn.util.collections.MutableSet;
import org.apache.brooklyn.util.core.ResourceUtils;
import org.apache.brooklyn.util.core.osgi.Osgis;
import org.apache.brooklyn.util.core.osgi.Osgis.ManifestHelper;
import org.apache.brooklyn.util.exceptions.Exceptions;
import org.apache.brooklyn.util.maven.MavenArtifact;
import org.apache.brooklyn.util.maven.MavenRetriever;
import org.apache.brooklyn.util.net.Urls;
import org.apache.brooklyn.util.os.Os;
import org.apache.brooklyn.util.stream.Streams;
import org.apache.commons.io.FileUtils;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.launch.Framework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Assert;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
/**
* Tests some assumptions about OSGi behaviour, in standalone mode (not part of brooklyn).
* See {@link OsgiTestResources} for description of test resources.
*/
public class OsgiStandaloneTest {
private static final Logger log = LoggerFactory.getLogger(OsgiStandaloneTest.class);
public static final String BROOKLYN_OSGI_TEST_A_0_1_0_PATH = OsgiTestResources.BROOKLYN_OSGI_TEST_A_0_1_0_PATH;
public static final String BROOKLYN_OSGI_TEST_A_0_1_0_URL = "classpath:"+BROOKLYN_OSGI_TEST_A_0_1_0_PATH;
public static final String BROOKLYN_TEST_OSGI_ENTITIES_PATH = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_PATH;
public static final String BROOKLYN_TEST_OSGI_ENTITIES_URL = "classpath:"+BROOKLYN_TEST_OSGI_ENTITIES_PATH;
public static final String BROOKLYN_TEST_OSGI_ENTITIES_NAME = "org.apache.brooklyn.test.resources.osgi.brooklyn-test-osgi-entities";
public static final String BROOKLYN_TEST_OSGI_ENTITIES_VERSION = "0.1.0";
protected Framework framework = null;
private File storageTempDir;
@BeforeMethod(alwaysRun=true)
public void setUp() throws Exception {
storageTempDir = Os.newTempDir("osgi-standalone");
framework = Osgis.newFrameworkStarted(storageTempDir.getAbsolutePath(), true, null);
}
@AfterMethod(alwaysRun=true)
public void tearDown() throws BundleException, IOException, InterruptedException {
tearDownOsgiFramework(framework, storageTempDir);
}
public static void tearDownOsgiFramework(Framework framework, File storageTempDir) throws BundleException, InterruptedException, IOException {
if (framework!=null) {
framework.stop();
Assert.assertEquals(framework.waitForStop(1000).getType(), FrameworkEvent.STOPPED);
framework = null;
}
if (storageTempDir!=null) {
FileUtils.deleteDirectory(storageTempDir);
storageTempDir = null;
}
}
protected Bundle install(String url) throws BundleException {
try {
return Osgis.install(framework, url);
} catch (Exception e) {
throw new IllegalStateException("test resources not available; may be an IDE issue, so try a mvn rebuild of this project", e);
}
}
protected Bundle installFromClasspath(String resourceName) throws BundleException {
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), resourceName);
try {
return Osgis.install(framework, String.format("classpath:%s", resourceName));
} catch (Exception e) {
throw Exceptions.propagate(e);
}
}
@Test
public void testInstallBundle() throws Exception {
Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
checkMath(bundle, 3, 6);
}
@Test
public void testBootBundle() throws Exception {
Bundle bundle = installFromClasspath(BROOKLYN_TEST_OSGI_ENTITIES_PATH);
Class<?> bundleCls = bundle.loadClass("org.apache.brooklyn.test.osgi.entities.SimpleEntity");
Assert.assertEquals(Entity.class, bundle.loadClass(Entity.class.getName()));
Assert.assertEquals(Entity.class, bundleCls.getClassLoader().loadClass(Entity.class.getName()));
}
@Test
public void testDuplicateBundle() throws Exception {
MavenArtifact artifact = new MavenArtifact("org.apache.brooklyn", "brooklyn-api", "jar", "0.8.0-incubating"); // BROOKLYN_VERSION
String localUrl = MavenRetriever.localUrl(artifact);
if ("file".equals(Urls.getProtocol(localUrl))) {
helperDuplicateBundle(localUrl);
} else {
log.warn("Skipping test OsgiStandaloneTest.testDuplicateBundle due to " + artifact + " not available in local repo.");
}
}
@Test(groups="Integration")
public void testRemoteDuplicateBundle() throws Exception {
helperDuplicateBundle(MavenRetriever.hostedUrl(new MavenArtifact("org.apache.brooklyn", "brooklyn-api", "jar", "0.8.0-incubating"))); // BROOKLYN_VERSION
}
public void helperDuplicateBundle(String url) throws Exception {
//The bundle is already installed from the boot path.
//Make sure that we still get the initially loaded
//bundle after trying to install the same version.
Bundle bundle = install(url);
Assert.assertTrue(Osgis.isExtensionBundle(bundle));
}
@Test
public void testAMultiplier() throws Exception {
Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
checkMath(bundle, 3, 6);
setAMultiplier(bundle, 5);
checkMath(bundle, 3, 15);
}
/** run two multiplier tests to ensure that irrespective of order the tests run in,
* on a fresh install the multiplier is reset */
@Test
public void testANOtherMultiple() throws Exception {
Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
checkMath(bundle, 3, 6);
setAMultiplier(bundle, 14);
checkMath(bundle, 3, 42);
}
@Test
public void testGetBundle() throws Exception {
Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
setAMultiplier(bundle, 3);
// can look it up based on the same location string (no other "location identifier" reference string seems to work here, however)
Bundle bundle2 = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
checkMath(bundle2, 3, 9);
}
@Test
public void testUninstallAndReinstallBundle() throws Exception {
Bundle bundle = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
checkMath(bundle, 3, 6);
setAMultiplier(bundle, 3);
checkMath(bundle, 3, 9);
bundle.uninstall();
Bundle bundle2 = installFromClasspath(BROOKLYN_OSGI_TEST_A_0_1_0_PATH);
checkMath(bundle2, 3, 6);
}
protected void checkMath(Bundle bundle, int input, int output) throws Exception {
Assert.assertNotNull(bundle);
Class<?> aClass = bundle.loadClass("brooklyn.test.osgi.TestA");
Object aInst = aClass.newInstance();
Object result = aClass.getMethod("times", int.class).invoke(aInst, input);
Assert.assertEquals(result, output);
}
protected void setAMultiplier(Bundle bundle, int newMultiplier) throws Exception {
Assert.assertNotNull(bundle);
Class<?> aClass = bundle.loadClass("brooklyn.test.osgi.TestA");
aClass.getField("multiplier").set(null, newMultiplier);
}
@Test
public void testReadAManifest() throws Exception {
Enumeration<URL> manifests = getClass().getClassLoader().getResources("META-INF/MANIFEST.MF");
log.info("Bundles and exported packages:");
MutableSet<String> allPackages = MutableSet.of();
while (manifests.hasMoreElements()) {
ManifestHelper mf = Osgis.ManifestHelper.forManifestContents(Streams.readFullyString( manifests.nextElement().openStream() ));
List<String> mfPackages = mf.getExportedPackages();
log.info(" "+mf.getSymbolicNameVersion()+": "+mfPackages);
allPackages.addAll(mfPackages);
}
log.info("Total export package count: "+allPackages.size());
Assert.assertTrue(allPackages.size()>20, "did not find enough packages"); // probably much larger
Assert.assertTrue(allPackages.contains(Osgis.class.getPackage().getName()));
}
@Test
public void testReadKnownManifest() throws Exception {
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), BROOKLYN_TEST_OSGI_ENTITIES_PATH);
InputStream in = this.getClass().getResourceAsStream(BROOKLYN_TEST_OSGI_ENTITIES_PATH);
JarInputStream jarIn = new JarInputStream(in);
ManifestHelper helper = Osgis.ManifestHelper.forManifest(jarIn.getManifest());
jarIn.close();
Assert.assertEquals(helper.getVersion().toString(), "0.1.0");
Assert.assertTrue(helper.getExportedPackages().contains("org.apache.brooklyn.test.osgi.entities"));
}
@Test
public void testLoadOsgiBundleDependencies() throws Exception {
Bundle bundle = installFromClasspath(BROOKLYN_TEST_OSGI_ENTITIES_PATH);
Assert.assertNotNull(bundle);
Class<?> aClass = bundle.loadClass("org.apache.brooklyn.test.osgi.entities.SimpleApplicationImpl");
Object aInst = aClass.newInstance();
Assert.assertNotNull(aInst);
}
@Test
public void testLoadAbsoluteWindowsResourceWithInstalledOSGi() {
//Felix installs an additional URL to the system classloader
//which throws an IllegalArgumentException when passed a
//windows path. See ExtensionManager.java static initializer.
String context = "mycontext";
String dummyPath = "C:\\dummypath";
ResourceUtils utils = ResourceUtils.create(this, context);
try {
utils.getResourceFromUrl(dummyPath);
Assert.fail("Non-reachable, should throw an exception for non-existing resource.");
} catch (RuntimeException e) {
Assert.assertTrue(e.getMessage().startsWith("Error getting resource '"+dummyPath+"' for "+context));
}
}
}