Java tutorial
/* * 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.util.core; import static com.google.common.base.Preconditions.checkNotNull; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertNull; import static org.testng.Assert.assertTrue; import java.io.ByteArrayInputStream; import java.net.URL; import java.util.jar.Attributes; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import javax.annotation.Nullable; import org.apache.brooklyn.api.catalog.CatalogItem.CatalogBundle; import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.entity.EntitySpec; import org.apache.brooklyn.api.mgmt.ManagementContext; import org.apache.brooklyn.core.BrooklynVersion; import org.apache.brooklyn.core.catalog.internal.CatalogBundleDto; import org.apache.brooklyn.core.catalog.internal.CatalogEntityItemDto; import org.apache.brooklyn.core.catalog.internal.CatalogItemBuilder; import org.apache.brooklyn.core.entity.AbstractEntity; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.entity.EntityInternal; import org.apache.brooklyn.core.mgmt.ha.OsgiManager; import org.apache.brooklyn.core.mgmt.internal.LocalManagementContext; import org.apache.brooklyn.core.mgmt.internal.ManagementContextInternal; import org.apache.brooklyn.core.mgmt.osgi.OsgiStandaloneTest; import org.apache.brooklyn.core.test.entity.LocalManagementContextForTests; import org.apache.brooklyn.entity.stock.BasicEntity; import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.test.support.TestResourceUnavailableException; import org.apache.brooklyn.util.core.osgi.Osgis; import org.apache.brooklyn.util.exceptions.Exceptions; import org.apache.brooklyn.util.guava.Maybe; import org.apache.brooklyn.util.maven.MavenArtifact; import org.apache.brooklyn.util.maven.MavenRetriever; import org.apache.brooklyn.util.osgi.OsgiTestResources; import org.apache.commons.io.output.ByteArrayOutputStream; import org.osgi.framework.Bundle; import org.osgi.framework.launch.Framework; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; import com.google.common.collect.ImmutableList; public class ClassLoaderUtilsTest { private LocalManagementContext mgmt; private String origWhiteListKey; @BeforeMethod(alwaysRun = true) public void setUp() throws Exception { origWhiteListKey = System.getProperty(ClassLoaderUtils.WHITE_LIST_KEY); } @AfterMethod(alwaysRun = true) public void tearDown() throws Exception { if (origWhiteListKey != null) { System.setProperty(ClassLoaderUtils.WHITE_LIST_KEY, origWhiteListKey); } else { System.clearProperty(ClassLoaderUtils.WHITE_LIST_KEY); } if (mgmt != null) { Entities.destroyAll(mgmt); } } @Test public void testLoadClassNotInOsgi() throws Exception { ClassLoaderUtils clu = new ClassLoaderUtils(getClass()); assertLoadSucceeds(clu, getClass().getName(), getClass()); assertLoadSucceeds(clu, Entity.class.getName(), Entity.class); assertLoadSucceeds(clu, AbstractEntity.class.getName(), AbstractEntity.class); assertLoadFails(clu, "org.apache.brooklyn.this.name.does.not.Exist"); } @Test public void testLoadClassInOsgi() throws Exception { String bundlePath = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH; String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; String classname = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY; TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), bundlePath); mgmt = LocalManagementContextForTests.builder(true).enableOsgiReusable().build(); Bundle bundle = installBundle(mgmt, bundleUrl); @SuppressWarnings("unchecked") Class<? extends Entity> clazz = (Class<? extends Entity>) bundle.loadClass(classname); Entity entity = createSimpleEntity(bundleUrl, clazz); System.setProperty(ClassLoaderUtils.WHITE_LIST_KEY, "do.not.match.whitelist:1.0.0"); ClassLoaderUtils cluMgmt = new ClassLoaderUtils(getClass(), mgmt); ClassLoaderUtils cluClass = new ClassLoaderUtils(clazz); ClassLoaderUtils cluEntity = new ClassLoaderUtils(getClass(), entity); assertLoadFails(classname, cluMgmt); assertLoadSucceeds(bundle.getSymbolicName() + ":" + classname, clazz, cluMgmt, cluClass, cluEntity); assertLoadSucceeds(bundle.getSymbolicName() + ":" + bundle.getVersion() + ":" + classname, clazz, cluMgmt, cluClass, cluEntity); } @Test public void testLoadJustOneClassInOsgiWhiteList() throws Exception { String bundlePath = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH; String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; String classname = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY; TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), bundlePath); mgmt = LocalManagementContextForTests.builder(true).enableOsgiReusable().build(); Bundle bundle = installBundle(mgmt, bundleUrl); Class<?> clazz = bundle.loadClass(classname); Entity entity = createSimpleEntity(bundleUrl, clazz); String whiteList = bundle.getSymbolicName() + ":" + bundle.getVersion(); System.setProperty(ClassLoaderUtils.WHITE_LIST_KEY, whiteList); ClassLoaderUtils cluEntity = new ClassLoaderUtils(getClass(), entity); BundledName resource = new BundledName(classname).toResource(); BundledName bn = new BundledName(resource.bundle, resource.version, "/" + resource.name); Asserts.assertSize(cluEntity.getResources(bn.toString()), 1); } @Test public void testVariousLoadersLoadClassInOsgiWhiteList() throws Exception { String bundlePath = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH; String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; String classname = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY; TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), bundlePath); mgmt = LocalManagementContextForTests.builder(true).enableOsgiReusable().build(); Bundle bundle = installBundle(mgmt, bundleUrl); Class<?> clazz = bundle.loadClass(classname); Entity entity = createSimpleEntity(bundleUrl, clazz); String whiteList = bundle.getSymbolicName() + ":" + bundle.getVersion(); System.setProperty(ClassLoaderUtils.WHITE_LIST_KEY, whiteList); ClassLoaderUtils cluMgmt = new ClassLoaderUtils(getClass(), mgmt); ClassLoaderUtils cluClass = new ClassLoaderUtils(clazz); ClassLoaderUtils cluEntity = new ClassLoaderUtils(getClass(), entity); assertLoadSucceeds(classname, clazz, cluMgmt, cluClass, cluEntity); assertLoadSucceeds(bundle.getSymbolicName() + ":" + classname, clazz, cluMgmt, cluClass, cluEntity); } @Test public void testLoadClassInOsgiWhiteListWithInvalidBundlePresent() throws Exception { String bundlePath = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH; String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; String classname = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY; TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), bundlePath); mgmt = LocalManagementContextForTests.builder(true).enableOsgiReusable().build(); Bundle bundle = installBundle(mgmt, bundleUrl); Manifest manifest = new Manifest(); manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0"); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); JarOutputStream target = new JarOutputStream(buffer, manifest); target.close(); OsgiManager osgiManager = ((ManagementContextInternal) mgmt).getOsgiManager().get(); Framework framework = osgiManager.getFramework(); Bundle installedBundle = framework.getBundleContext().installBundle("stream://invalid", new ByteArrayInputStream(buffer.toByteArray())); assertNotNull(installedBundle); Class<?> clazz = bundle.loadClass(classname); Entity entity = createSimpleEntity(bundleUrl, clazz); String whileList = bundle.getSymbolicName() + ":" + bundle.getVersion(); System.setProperty(ClassLoaderUtils.WHITE_LIST_KEY, whileList); ClassLoaderUtils cluMgmt = new ClassLoaderUtils(getClass(), mgmt); ClassLoaderUtils cluClass = new ClassLoaderUtils(clazz); ClassLoaderUtils cluEntity = new ClassLoaderUtils(getClass(), entity); assertLoadSucceeds(classname, clazz, cluMgmt, cluClass, cluEntity); assertLoadSucceeds(bundle.getSymbolicName() + ":" + classname, clazz, cluMgmt, cluClass, cluEntity); } @Test public void testLoadClassInOsgiCore() throws Exception { Class<?> clazz = BasicEntity.class; String classname = clazz.getName(); mgmt = LocalManagementContextForTests.builder(true).enableOsgiReusable().build(); Bundle bundle = getBundle(mgmt, "org.apache.brooklyn.core"); String url = bundle.getLocation(); // NB: the above will be a system:file: url when running tests against target/classes/ -- but // OSGi manager will accept that if running in dev mode Entity entity = createSimpleEntity(url, clazz); ClassLoaderUtils cluMgmt = new ClassLoaderUtils(getClass(), mgmt); ClassLoaderUtils cluClass = new ClassLoaderUtils(clazz); ClassLoaderUtils cluNone = new ClassLoaderUtils(getClass()); ClassLoaderUtils cluEntity = new ClassLoaderUtils(getClass(), entity); assertLoadSucceeds(classname, clazz, cluMgmt, cluClass, cluNone, cluEntity); assertLoadSucceeds(classname, clazz, cluMgmt, cluClass, cluNone, cluEntity); assertLoadSucceeds(bundle.getSymbolicName() + ":" + classname, clazz, cluMgmt, cluClass, cluNone, cluEntity); assertLoadSucceeds(bundle.getSymbolicName() + ":" + bundle.getVersion() + ":" + classname, clazz, cluMgmt, cluClass, cluNone, cluEntity); } @Test public void testLoadClassInOsgiApi() throws Exception { Class<?> clazz = Entity.class; String classname = clazz.getName(); mgmt = LocalManagementContextForTests.builder(true).enableOsgiReusable().build(); Bundle bundle = getBundle(mgmt, "org.apache.brooklyn.api"); ClassLoaderUtils cluMgmt = new ClassLoaderUtils(getClass(), mgmt); ClassLoaderUtils cluClass = new ClassLoaderUtils(clazz); ClassLoaderUtils cluNone = new ClassLoaderUtils(getClass()); assertLoadSucceeds(classname, clazz, cluMgmt, cluClass, cluNone); assertLoadSucceeds(classname, clazz, cluMgmt, cluClass, cluNone); assertLoadSucceeds(bundle.getSymbolicName() + ":" + classname, clazz, cluMgmt, cluClass, cluNone); assertLoadSucceeds(bundle.getSymbolicName() + ":" + bundle.getVersion() + ":" + classname, clazz, cluMgmt, cluClass, cluNone); } @Test public void testIsBundleWhiteListed() throws Exception { mgmt = LocalManagementContextForTests.builder(true).enableOsgiReusable().build(); ClassLoaderUtils clu = new ClassLoaderUtils(getClass(), mgmt); assertTrue(clu.isBundleWhiteListed(getBundle(mgmt, "org.apache.brooklyn.core"))); assertTrue(clu.isBundleWhiteListed(getBundle(mgmt, "org.apache.brooklyn.api"))); assertFalse(clu.isBundleWhiteListed(getBundle(mgmt, "com.google.guava"))); } /** * When two guava versions installed, want us to load from the *brooklyn* version rather than * a newer version that happens to be in Karaf. */ @Test(groups = { "Integration" }) public void testLoadsFromRightGuavaVersion() throws Exception { mgmt = LocalManagementContextForTests.builder(true).enableOsgiReusable().build(); ClassLoaderUtils clu = new ClassLoaderUtils(getClass(), mgmt); String bundleUrl = MavenRetriever.localUrl(MavenArtifact.fromCoordinate("com.google.guava:guava:jar:18.0")); Bundle bundle = installBundle(mgmt, bundleUrl); String bundleName = bundle.getSymbolicName(); String classname = bundleName + ":" + ImmutableList.class.getName(); assertLoadSucceeds(clu, classname, ImmutableList.class); } @Test public void testLoadBrooklynClass() throws Exception { mgmt = LocalManagementContextForTests.builder(true).enableOsgiReusable().build(); new ClassLoaderUtils(this, mgmt).loadClass("org.apache.brooklyn.api", BrooklynVersion.get(), Entity.class.getName()); new ClassLoaderUtils(this, mgmt).loadClass("org.apache.brooklyn.api", BrooklynVersion.getOsgiVersion(), Entity.class.getName()); try { new ClassLoaderUtils(this, mgmt).loadClass("org.apache.brooklyn.api", "100.100.100-alpha-version_wth.tags", Entity.class.getName()); Asserts.shouldHaveFailedPreviously(); } catch (Exception e) { IllegalStateException nested = Exceptions.getFirstThrowableOfType(e, IllegalStateException.class); assertNotNull(nested); Asserts.expectedFailureContains(nested, "not found to load"); } try { new ClassLoaderUtils(this, mgmt).loadClass("org.apache.brooklyn.api", "100.100.100-SNAPSHOT", Entity.class.getName()); Asserts.shouldHaveFailedPreviously(); } catch (Exception e) { IllegalStateException nested = Exceptions.getFirstThrowableOfType(e, IllegalStateException.class); assertNotNull(nested); Asserts.expectedFailureContains(nested, "not found to load"); } } private Bundle installBundle(ManagementContext mgmt, String bundleUrl) throws Exception { OsgiManager osgiManager = ((ManagementContextInternal) mgmt).getOsgiManager().get(); Framework framework = osgiManager.getFramework(); return Osgis.install(framework, bundleUrl); } private Bundle getBundle(ManagementContext mgmt, final String symbolicName) throws Exception { OsgiManager osgiManager = ((ManagementContextInternal) mgmt).getOsgiManager().get(); Framework framework = osgiManager.getFramework(); Maybe<Bundle> result = Osgis.bundleFinder(framework).symbolicName(symbolicName).find(); return result.get(); } private void assertLoadSucceeds(String bundledClassName, Class<?> expectedClass, ClassLoaderUtils... clua) throws ClassNotFoundException { for (ClassLoaderUtils clu : clua) { try { assertLoadSucceeds(clu, bundledClassName, expectedClass); } catch (Exception e) { Exceptions.propagateIfFatal(e); throw new IllegalStateException("Load failed for " + clu, e); } } } private void assertLoadSucceeds(ClassLoaderUtils clu, String bundledClassName, Class<?> expectedClass) throws ClassNotFoundException { BundledName className = new BundledName(bundledClassName); Class<?> cls = clu.loadClass(bundledClassName); assertEquals(cls.getName(), className.name); if (expectedClass != null) { assertEquals(cls, expectedClass); } ClassLoader cl = cls.getClassLoader(); BundledName resource = className.toResource(); String bundledResource = resource.toString(); URL resourceUrl = cl.getResource(resource.name); assertEquals(clu.getResource(bundledResource), resourceUrl); assertEquals(clu.getResources(bundledResource), ImmutableList.of(resourceUrl), "Loading with " + clu); BundledName rootResource = new BundledName(resource.bundle, resource.version, "/" + resource.name); String rootBundledResource = rootResource.toString(); assertEquals(clu.getResource(rootBundledResource), resourceUrl); assertEquals(clu.getResources(rootBundledResource), ImmutableList.of(resourceUrl)); } private void assertLoadFails(String bundledClassName, ClassLoaderUtils... clua) { for (ClassLoaderUtils clu : clua) { assertLoadFails(clu, bundledClassName); } } private void assertLoadFails(ClassLoaderUtils clu, String bundledClassName) { BundledName className = new BundledName(bundledClassName); try { clu.loadClass(bundledClassName); Asserts.shouldHaveFailedPreviously("Using loader " + clu); } catch (ClassNotFoundException e) { Asserts.expectedFailureContains(e, bundledClassName, "not found on the application class path, nor in the bundle white list"); } BundledName resource = className.toResource(); String bundledResource = resource.toString(); assertNull(clu.getResource(bundledResource), resource + " is supposed to fail resource loading, but it was successful"); assertEquals(clu.getResources(bundledResource), ImmutableList.of(), resource + " is supposed to fail resource loading, but it was successful"); } protected Entity createSimpleEntity(String bundleUrl, Class<?> clazz) { @SuppressWarnings("unchecked") Class<? extends Entity> entityClass = (Class<? extends Entity>) clazz; EntitySpec<?> spec = EntitySpec.create(entityClass); Entity entity = mgmt.getEntityManager().createEntity(spec); CatalogEntityItemDto item = CatalogItemBuilder.newEntity(clazz.getName(), "1.0.0") .libraries(ImmutableList.<CatalogBundle>of(new CatalogBundleDto(null, null, bundleUrl))) .plan("{\"services\":[{\"type\": \"" + clazz.getName() + "\"}]}").build(); mgmt.getCatalog().addItem(item); final EntityInternal entityInternal = (EntityInternal) entity; entityInternal.setCatalogItemIdAndSearchPath(item.getCatalogItemId(), item.getCatalogItemIdSearchPath()); return entity; } private static class BundledName { String bundle; String version; String name; BundledName(String bundledName) { String[] arr = bundledName.split(":"); if (arr.length == 1) { bundle = null; version = null; name = arr[0]; } else if (arr.length == 2) { bundle = arr[0]; version = null; name = arr[1]; } else if (arr.length == 3) { bundle = arr[0]; version = arr[1]; name = arr[2]; } else { throw new IllegalStateException("Invalid bundled name " + bundledName); } } BundledName(@Nullable String bundle, @Nullable String version, String name) { this.bundle = bundle; this.version = version; this.name = checkNotNull(name, "name"); } @Override public String toString() { return (bundle != null ? bundle + ":" : "") + (version != null ? version + ":" : "") + name; } BundledName toResource() { return new BundledName(bundle, version, name.replace(".", "/") + ".class"); } } }