| /******************************************************************************* |
| * Copyright (c) 2013, 2017 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.osgi.tests.hooks.framework; |
| |
| import java.io.File; |
| import java.net.URL; |
| import java.util.*; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.TimeUnit; |
| import org.eclipse.osgi.internal.hookregistry.HookRegistry; |
| import org.eclipse.osgi.tests.OSGiTestsActivator; |
| import org.osgi.framework.*; |
| import org.osgi.framework.hooks.weaving.WeavingHook; |
| import org.osgi.framework.hooks.weaving.WovenClass; |
| import org.osgi.framework.launch.Framework; |
| import org.osgi.framework.namespace.PackageNamespace; |
| import org.osgi.framework.wiring.BundleRevision; |
| import org.osgi.framework.wiring.FrameworkWiring; |
| |
| public class ClassLoaderHookTests extends AbstractFrameworkHookTests { |
| private static final String TEST_BUNDLE = "substitutes.a"; |
| private static final String TEST_CLASSNAME = "substitutes.x.Ax"; |
| private static final String HOOK_CONFIGURATOR_BUNDLE = "classloader.hooks.a"; |
| private static final String HOOK_CONFIGURATOR_CLASS = "org.eclipse.osgi.tests.classloader.hooks.a.TestHookConfigurator"; |
| private static final String REJECT_PROP = "classloader.hooks.a.reject"; |
| private static final String BAD_TRANSFORM_PROP = "classloader.hooks.a.bad.transform"; |
| private static final String RECURSION_LOAD = "classloader.hooks.a.recursion.load"; |
| private static final String RECURSION_LOAD_SUPPORTED = "classloader.hooks.a.recursion.load.supported"; |
| |
| private Map<String, String> configuration; |
| private Framework framework; |
| private String location; |
| |
| protected void setUp() throws Exception { |
| super.setUp(); |
| setRejectTransformation(false); |
| setBadTransform(false); |
| setRecursionLoad(false); |
| setRecursionLoadSupported(false); |
| String loc = bundleInstaller.getBundleLocation(HOOK_CONFIGURATOR_BUNDLE); |
| loc = loc.substring(loc.indexOf("file:")); |
| classLoader.addURL(new URL(loc)); |
| location = bundleInstaller.getBundleLocation(TEST_BUNDLE); |
| File file = OSGiTestsActivator.getContext().getDataFile(getName()); |
| configuration = new HashMap<String, String>(); |
| configuration.put(Constants.FRAMEWORK_STORAGE, file.getAbsolutePath()); |
| configuration.put(HookRegistry.PROP_HOOK_CONFIGURATORS_INCLUDE, HOOK_CONFIGURATOR_CLASS); |
| framework = createFramework(configuration); |
| } |
| |
| protected void tearDown() throws Exception { |
| stopQuietly(framework); |
| super.tearDown(); |
| } |
| |
| private void initAndStartFramework() throws Exception { |
| initAndStart(framework); |
| } |
| |
| private Bundle installBundle() throws Exception { |
| return framework.getBundleContext().installBundle(location); |
| } |
| |
| private void setRejectTransformation(boolean value) { |
| System.setProperty(REJECT_PROP, Boolean.toString(value)); |
| } |
| |
| private void setBadTransform(boolean value) { |
| System.setProperty(BAD_TRANSFORM_PROP, Boolean.toString(value)); |
| } |
| |
| private void setRecursionLoad(boolean value) { |
| System.setProperty(RECURSION_LOAD, Boolean.toString(value)); |
| } |
| |
| private void setRecursionLoadSupported(boolean value) { |
| System.setProperty(RECURSION_LOAD_SUPPORTED, Boolean.toString(value)); |
| } |
| |
| public void testRejectTransformationFromWeavingHook() throws Exception { |
| setRejectTransformation(true); |
| initAndStartFramework(); |
| framework.getBundleContext().registerService(WeavingHook.class, new WeavingHook() { |
| |
| @Override |
| public void weave(WovenClass wovenClass) { |
| wovenClass.setBytes(new byte[] {'b', 'a', 'd', 'b', 'y', 't', 'e', 's'}); |
| wovenClass.getDynamicImports().add("badimport"); |
| } |
| }, null); |
| Bundle b = installBundle(); |
| b.loadClass(TEST_CLASSNAME); |
| // class load must succeed because the badbytes got rejected |
| // make sure we don't have any dynamic imports added |
| assertEquals("Found some imports.", 0, b.adapt(BundleRevision.class).getWiring().getRequirements(PackageNamespace.PACKAGE_NAMESPACE).size()); |
| |
| // no don't reject |
| setRejectTransformation(false); |
| refreshBundles(Collections.singleton(b)); |
| try { |
| b.loadClass(TEST_CLASSNAME); |
| fail("Expected a ClassFormatError."); |
| } catch (ClassFormatError e) { |
| // expected |
| } |
| // class load must fail because the badbytes got used to define the class |
| // make sure we have a dynamic imports added |
| assertEquals("Found some imports.", 1, b.adapt(BundleRevision.class).getWiring().getRequirements(PackageNamespace.PACKAGE_NAMESPACE).size()); |
| } |
| |
| public void testRejectTransformationFromClassLoadingHook() throws Exception { |
| setRejectTransformation(true); |
| setBadTransform(true); |
| initAndStartFramework(); |
| Bundle b = installBundle(); |
| b.loadClass(TEST_CLASSNAME); |
| |
| // no don't reject |
| setRejectTransformation(false); |
| refreshBundles(Collections.singleton(b)); |
| try { |
| b.loadClass(TEST_CLASSNAME); |
| fail("Expected a ClassFormatError."); |
| } catch (ClassFormatError e) { |
| // expected |
| } |
| } |
| |
| public void testRecursionFromClassLoadingHookNotSupported() throws Exception { |
| setRecursionLoad(true); |
| initAndStartFramework(); |
| Bundle b = installBundle(); |
| b.loadClass(TEST_CLASSNAME); |
| } |
| |
| public void testRecursionFromClassLoadingHookIsSupported() throws Exception { |
| setRecursionLoad(true); |
| setRecursionLoadSupported(true); |
| initAndStartFramework(); |
| Bundle b = installBundle(); |
| b.loadClass(TEST_CLASSNAME); |
| } |
| |
| private void refreshBundles(Collection<Bundle> bundles) throws InterruptedException { |
| final CountDownLatch refreshSignal = new CountDownLatch(1); |
| framework.adapt(FrameworkWiring.class).refreshBundles(bundles, new FrameworkListener() { |
| |
| @Override |
| public void frameworkEvent(FrameworkEvent event) { |
| if (event.getType() == FrameworkEvent.PACKAGES_REFRESHED) { |
| refreshSignal.countDown(); |
| } |
| } |
| }); |
| refreshSignal.await(30, TimeUnit.SECONDS); |
| } |
| } |