Repository: brooklyn-server Updated Branches: refs/heads/master 20c6b1e00 -> e3f4175ce
Adds OSGi classloading/rebind tests Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/c68bfd5a Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/c68bfd5a Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/c68bfd5a Branch: refs/heads/master Commit: c68bfd5ac500f5e9f24fa87bf73edacb19e80490 Parents: 33421d7 Author: Aled Sage <[email protected]> Authored: Wed Jul 6 16:06:24 2016 +0100 Committer: Aled Sage <[email protected]> Committed: Wed Jul 6 16:06:24 2016 +0100 ---------------------------------------------------------------------- .../brooklyn/catalog/CatalogYamlRebindTest.java | 150 +++++++++++------- .../brooklyn/util/core/ClassLoaderUtils.java | 8 +- .../core/mgmt/rebind/RebindTestFixture.java | 6 + .../util/core/ClassLoaderUtilsTest.java | 158 +++++++++++++++++++ 4 files changed, 266 insertions(+), 56 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c68bfd5a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java ---------------------------------------------------------------------- diff --git a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java index b91cd11..d740560 100644 --- a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java +++ b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/catalog/CatalogYamlRebindTest.java @@ -37,6 +37,7 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.brooklyn.api.catalog.CatalogItem; +import org.apache.brooklyn.api.entity.Entity; import org.apache.brooklyn.api.mgmt.rebind.mementos.BrooklynMementoPersister; import org.apache.brooklyn.api.policy.Policy; import org.apache.brooklyn.api.sensor.Enricher; @@ -46,6 +47,7 @@ import org.apache.brooklyn.core.BrooklynFeatureEnablement; import org.apache.brooklyn.core.catalog.internal.CatalogUtils; import org.apache.brooklyn.core.entity.Entities; import org.apache.brooklyn.core.entity.StartableApplication; +import org.apache.brooklyn.core.mgmt.osgi.OsgiStandaloneTest; import org.apache.brooklyn.core.mgmt.persist.BrooklynMementoPersisterToObjectStore; import org.apache.brooklyn.core.mgmt.persist.PersistenceObjectStore; import org.apache.brooklyn.core.mgmt.persist.PersistenceObjectStore.StoreObjectAccessor; @@ -55,7 +57,11 @@ import org.apache.brooklyn.core.test.policy.TestPolicy; import org.apache.brooklyn.entity.stock.BasicEntity; import org.apache.brooklyn.test.Asserts; import org.apache.brooklyn.util.exceptions.Exceptions; +import org.apache.brooklyn.util.osgi.OsgiTestResources; import org.apache.brooklyn.util.text.Strings; +import org.testng.annotations.AfterMethod; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; import org.testng.annotations.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -75,7 +81,11 @@ public class CatalogYamlRebindTest extends AbstractYamlRebindTest { // - entities cannot be instantiated because class no longer on classpath (e.g. was OSGi) // - config/attribute cannot be instantiated (e.g. because class no longer on classpath) // - entity file corrupt - + + private static final String OSGI_BUNDLE_URL = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; + private static final String OSGI_SIMPLE_ENTITY_TYPE = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY; + private static final String OSGI_SIMPLE_POLICY_TYPE = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_POLICY; + enum RebindWithCatalogTestMode { NO_OP, STRIP_DEPRECATION_AND_ENABLEMENT_FROM_CATALOG_ITEM, @@ -85,63 +95,97 @@ public class CatalogYamlRebindTest extends AbstractYamlRebindTest { REPLACE_CATALOG_WITH_NEWER_VERSION; } - @Test - public void testRebindWithCatalogAndApp() throws Exception { - runRebindWithCatalogAndApp(RebindWithCatalogTestMode.NO_OP); - } + private Boolean defaultEnablementOfFeatureAutoFixatalogRefOnRebind; - @Test - public void testRebindWithCatalogDeprecatedAndAppExisting() throws Exception { - runRebindWithCatalogAndApp(RebindWithCatalogTestMode.DEPRECATE_CATALOG); + @BeforeMethod(alwaysRun=true) + @Override + public void setUp() throws Exception { + defaultEnablementOfFeatureAutoFixatalogRefOnRebind = BrooklynFeatureEnablement.isEnabled(BrooklynFeatureEnablement.FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND); + super.setUp(); } - @Test - public void testRebindWithCatalogDisabledAndAppExisting() throws Exception { - runRebindWithCatalogAndApp(RebindWithCatalogTestMode.DISABLE_CATALOG); - } - - // See https://issues.apache.org/jira/browse/BROOKLYN-149. - // Deletes the catalog item before rebind, but the referenced types are still on the - // default classpath. - // Will fallback to loading from classpath. - @Test - public void testRebindWithCatalogDeletedAndAppExisting() throws Exception { - runRebindWithCatalogAndApp(RebindWithCatalogTestMode.DELETE_CATALOG); + @AfterMethod(alwaysRun=true) + @Override + public void tearDown() throws Exception { + if (defaultEnablementOfFeatureAutoFixatalogRefOnRebind != null) { + BrooklynFeatureEnablement.setEnablement(BrooklynFeatureEnablement.FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND, defaultEnablementOfFeatureAutoFixatalogRefOnRebind); + } + super.tearDown(); } - // Upgrades the catalog item before rebind, deleting the old version. - // Will automatically upgrade. - @Test - public void testRebindWithCatalogUpgradedWithOldDeletedAndAppExisting() throws Exception { - BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND); - runRebindWithCatalogAndApp(RebindWithCatalogTestMode.REPLACE_CATALOG_WITH_NEWER_VERSION); + protected boolean useOsgi() { + return true; } - - /** - * Old persisted state for catalog items may not have a "deprecated" or "disabled" - * value. Need to check that their absence will default to false. - */ - @Test - public void testRebindWithCatalogPropertiesForDeprecationAndEnablementAbsent() throws Exception { - runRebindWithCatalogAndApp(RebindWithCatalogTestMode.STRIP_DEPRECATION_AND_ENABLEMENT_FROM_CATALOG_ITEM); + + @DataProvider + public Object[][] dataProvider() { + return new Object[][] { + {RebindWithCatalogTestMode.NO_OP, false}, + {RebindWithCatalogTestMode.NO_OP, true}, + + {RebindWithCatalogTestMode.STRIP_DEPRECATION_AND_ENABLEMENT_FROM_CATALOG_ITEM, false}, + {RebindWithCatalogTestMode.STRIP_DEPRECATION_AND_ENABLEMENT_FROM_CATALOG_ITEM, true}, + + {RebindWithCatalogTestMode.DEPRECATE_CATALOG, false}, + {RebindWithCatalogTestMode.DEPRECATE_CATALOG, true}, + + {RebindWithCatalogTestMode.DISABLE_CATALOG, false}, + {RebindWithCatalogTestMode.DISABLE_CATALOG, true}, + + // For DELETE_CATALOG, see https://issues.apache.org/jira/browse/BROOKLYN-149. + // Deletes the catalog item before rebind, but the referenced types are still on the + // default classpath. Will fallback to loading from classpath. + // + // Does not work for OSGi, because our bundle will no longer be available. + {RebindWithCatalogTestMode.DELETE_CATALOG, false}, + + // Upgrades the catalog item before rebind, deleting the old version. + // Will automatically upgrade. Test will enable "FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND" + {RebindWithCatalogTestMode.REPLACE_CATALOG_WITH_NEWER_VERSION, false}, + {RebindWithCatalogTestMode.REPLACE_CATALOG_WITH_NEWER_VERSION, true}, + }; } - @SuppressWarnings({ "unused", "deprecation" }) - protected void runRebindWithCatalogAndApp(RebindWithCatalogTestMode mode) throws Exception { + @Test(dataProvider = "dataProvider") + @SuppressWarnings("deprecation") + public void testRebindWithCatalogAndApp(RebindWithCatalogTestMode mode, boolean useOsgi) throws Exception { + if (mode == RebindWithCatalogTestMode.REPLACE_CATALOG_WITH_NEWER_VERSION) { + BrooklynFeatureEnablement.enable(BrooklynFeatureEnablement.FEATURE_AUTO_FIX_CATALOG_REF_ON_REBIND); + } + String appSymbolicName = "my.catalog.app.id.load"; String appVersion = "0.1.0"; - String appCatalogFormat = Joiner.on("\n").join( - "brooklyn.catalog:", - " id: " + appSymbolicName, - " version: %s", - " itemType: entity", - " item:", - " type: "+ BasicEntity.class.getName(), - " brooklyn.enrichers:", - " - type: "+TestEnricher.class.getName(), - " brooklyn.policies:", - " - type: "+TestPolicy.class.getName()); + String appCatalogFormat; + if (useOsgi) { + appCatalogFormat = Joiner.on("\n").join( + "brooklyn.catalog:", + " id: " + appSymbolicName, + " version: %s", + " itemType: entity", + " libraries:", + " - url: " + OSGI_BUNDLE_URL, + " item:", + " type: " + OSGI_SIMPLE_ENTITY_TYPE, + " brooklyn.enrichers:", + " - type: " + TestEnricher.class.getName(), + " brooklyn.policies:", + " - type: " + OSGI_SIMPLE_POLICY_TYPE); + } else { + appCatalogFormat = Joiner.on("\n").join( + "brooklyn.catalog:", + " id: " + appSymbolicName, + " version: %s", + " itemType: entity", + " item:", + " type: "+ BasicEntity.class.getName(), + " brooklyn.enrichers:", + " - type: "+TestEnricher.class.getName(), + " brooklyn.policies:", + " - type: "+TestPolicy.class.getName()); + } + + String locSymbolicName = "my.catalog.loc.id.load"; String locVersion = "1.0.0"; String locCatalogFormat = Joiner.on("\n").join( @@ -164,9 +208,9 @@ public class CatalogYamlRebindTest extends AbstractYamlRebindTest { "services: \n" + "- type: "+CatalogUtils.getVersionedId(appSymbolicName, appVersion); origApp = (StartableApplication) createAndStartApplication(yaml); - BasicEntity origEntity = (BasicEntity) Iterables.getOnlyElement(origApp.getChildren()); - TestPolicy origPolicy = (TestPolicy) Iterables.getOnlyElement(origEntity.policies()); - TestEnricher origEnricher = (TestEnricher) Iterables.tryFind(origEntity.enrichers(), Predicates.instanceOf(TestEnricher.class)).get(); + Entity origEntity = Iterables.getOnlyElement(origApp.getChildren()); + Policy origPolicy = Iterables.getOnlyElement(origEntity.policies()); + Enricher origEnricher = Iterables.tryFind(origEntity.enrichers(), Predicates.instanceOf(TestEnricher.class)).get(); assertEquals(origEntity.getCatalogItemId(), appSymbolicName+":"+appVersion); // Depending on test-mode, delete the catalog item, and then rebind @@ -226,7 +270,7 @@ public class CatalogYamlRebindTest extends AbstractYamlRebindTest { } // Ensure app is still there, and that it is usable - e.g. "stop" effector functions as expected - BasicEntity newEntity = (BasicEntity) Iterables.getOnlyElement(newApp.getChildren()); + Entity newEntity = Iterables.getOnlyElement(newApp.getChildren()); Policy newPolicy = Iterables.getOnlyElement(newEntity.policies()); Enricher newEnricher = Iterables.tryFind(newEntity.enrichers(), Predicates.instanceOf(TestEnricher.class)).get(); assertEquals(newEntity.getCatalogItemId(), appSymbolicName+":"+appVersion); @@ -279,12 +323,12 @@ public class CatalogYamlRebindTest extends AbstractYamlRebindTest { if (itemDeployable) { StartableApplication app2 = (StartableApplication) createAndStartApplication(yaml2); - BasicEntity entity2 = (BasicEntity) Iterables.getOnlyElement(app2.getChildren()); + Entity entity2 = Iterables.getOnlyElement(app2.getChildren()); assertEquals(entity2.getCatalogItemId(), appSymbolicName+":"+appVersion); } else { try { StartableApplication app2 = (StartableApplication) createAndStartApplication(yaml2); - Asserts.shouldHaveFailedPreviously(); + Asserts.shouldHaveFailedPreviously("app2="+app2); } catch (Exception e) { // only these two modes are allowed; may have different assertions (but don't yet) if (mode == RebindWithCatalogTestMode.DELETE_CATALOG) { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c68bfd5a/core/src/main/java/org/apache/brooklyn/util/core/ClassLoaderUtils.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/util/core/ClassLoaderUtils.java b/core/src/main/java/org/apache/brooklyn/util/core/ClassLoaderUtils.java index 5bbce8d..644f07a 100644 --- a/core/src/main/java/org/apache/brooklyn/util/core/ClassLoaderUtils.java +++ b/core/src/main/java/org/apache/brooklyn/util/core/ClassLoaderUtils.java @@ -44,14 +44,16 @@ import org.slf4j.LoggerFactory; import com.google.common.base.Predicate; public class ClassLoaderUtils { + + private static final Logger log = LoggerFactory.getLogger(ClassLoaderUtils.class); + /** * White list format should be * <bundle symbolic name regex>[:<bundle version regex in dotted format (X.X.X.SNAPSHOT, instead of X.X.X-SNAPSHOT)>] */ + static final String WHITE_LIST_KEY = "org.apache.brooklyn.classloader.fallback.bundles"; + static final String CLASS_NAME_DELIMITER = ":"; private static final String WHITE_LIST_DEFAULT = "org\\.apache\\.brooklyn\\..*:" + BrooklynVersion.get().replaceFirst("-", "."); - private static final String WHITE_LIST_KEY = "org.apache.brooklyn.classloader.fallback.bundles"; - private static final String CLASS_NAME_DELIMITER = ":"; - private static final Logger log = LoggerFactory.getLogger(ClassLoaderUtils.class); // Class.forName gets the class loader from the calling class. // We don't have access to the same reflection API so need to pass it explicitly. http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c68bfd5a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestFixture.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestFixture.java b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestFixture.java index 8bbd04d..4744708 100644 --- a/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestFixture.java +++ b/core/src/test/java/org/apache/brooklyn/core/mgmt/rebind/RebindTestFixture.java @@ -102,6 +102,7 @@ public abstract class RebindTestFixture<T extends StartableApplication> { .forLive(useLiveManagementContext()) .emptyCatalog(useEmptyCatalog()) .properties(createBrooklynProperties()) + .enableOsgi(useOsgi()) .buildStarted(); } @@ -117,6 +118,7 @@ public abstract class RebindTestFixture<T extends StartableApplication> { .forLive(useLiveManagementContext()) .emptyCatalog(useEmptyCatalog()) .properties(createBrooklynProperties()) + .enableOsgi(useOsgi()) .buildUnstarted(); } @@ -176,6 +178,10 @@ public abstract class RebindTestFixture<T extends StartableApplication> { return true; } + protected boolean useOsgi() { + return false; + } + protected int getPersistPeriodMillis() { return 1; } http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/c68bfd5a/core/src/test/java/org/apache/brooklyn/util/core/ClassLoaderUtilsTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/util/core/ClassLoaderUtilsTest.java b/core/src/test/java/org/apache/brooklyn/util/core/ClassLoaderUtilsTest.java new file mode 100644 index 0000000..bfa5adc --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/util/core/ClassLoaderUtilsTest.java @@ -0,0 +1,158 @@ +/* + * 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 org.testng.Assert.assertEquals; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.mgmt.ManagementContext; +import org.apache.brooklyn.core.entity.Entities; +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.test.Asserts; +import org.apache.brooklyn.util.core.osgi.Osgis; +import org.apache.brooklyn.util.guava.Maybe; +import org.apache.brooklyn.util.osgi.OsgiTestResources; +import org.dom4j.tree.AbstractEntity; +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; + +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()); + assertEquals(clu.loadClass(getClass().getName()), getClass()); + assertEquals(clu.loadClass(Entity.class.getName()), Entity.class); + assertEquals(clu.loadClass(AbstractEntity.class.getName()), AbstractEntity.class); + assertLoadFails(clu, "org.apache.brooklyn.this.name.does.not.Exist"); + } + + @Test + public void testLoadClassInOsgi() throws Exception { + String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; + String classname = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY; + + mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build(); + Bundle bundle = installBundle(mgmt, bundleUrl); + + ClassLoaderUtils clu = new ClassLoaderUtils(getClass(), mgmt); + + assertLoadFails(clu, classname); + assertEquals(clu.loadClass(bundle.getSymbolicName() + ":" + classname).getName(), classname); + assertEquals(clu.loadClass(bundle.getSymbolicName() + ":" + bundle.getVersion()+":" + classname).getName(), classname); + } + + @Test + public void testLoadClassInOsgiWhiteList() throws Exception { + String bundleUrl = OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_URL; + String classname = OsgiTestResources.BROOKLYN_TEST_OSGI_ENTITIES_SIMPLE_ENTITY; + + mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build(); + Bundle bundle = installBundle(mgmt, bundleUrl); + + String whileList = bundle.getSymbolicName()+":"+bundle.getVersion(); + System.setProperty(ClassLoaderUtils.WHITE_LIST_KEY, whileList); + + ClassLoaderUtils clu = new ClassLoaderUtils(getClass(), mgmt); + assertEquals(clu.loadClass(classname).getName(), classname); + assertEquals(clu.loadClass(bundle.getSymbolicName() + ":" + classname).getName(), classname); + } + + @Test + public void testLoadClassInOsgiCore() throws Exception { + Class<?> clazz = AbstractEntity.class; + String classname = clazz.getName(); + + mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build(); + Bundle bundle = getBundle(mgmt, "org.apache.brooklyn.core"); + + ClassLoaderUtils clu = new ClassLoaderUtils(getClass(), mgmt); + assertEquals(clu.loadClass(classname), clazz); + assertEquals(clu.loadClass(classname), clazz); + assertEquals(clu.loadClass(bundle.getSymbolicName() + ":" + classname), clazz); + assertEquals(clu.loadClass(bundle.getSymbolicName() + ":" + bundle.getVersion() + ":" + classname), clazz); + } + + @Test + public void testLoadClassInOsgiApi() throws Exception { + Class<?> clazz = Entity.class; + String classname = clazz.getName(); + + mgmt = LocalManagementContextForTests.builder(true).disableOsgi(false).build(); + Bundle bundle = getBundle(mgmt, "org.apache.brooklyn.api"); + + ClassLoaderUtils clu = new ClassLoaderUtils(getClass(), mgmt); + assertEquals(clu.loadClass(classname), clazz); + assertEquals(clu.loadClass(classname), clazz); + assertEquals(clu.loadClass(bundle.getSymbolicName() + ":" + classname), clazz); + assertEquals(clu.loadClass(bundle.getSymbolicName() + ":" + bundle.getVersion() + ":" + classname), clazz); + } + + 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 assertLoadFails(ClassLoaderUtils clu, String className) { + try { + clu.loadClass(className); + Asserts.shouldHaveFailedPreviously(); + } catch (ClassNotFoundException e) { + Asserts.expectedFailureContains(e, className, "not found on the application class path, nor in the bundle white list"); + } + } +}
