/* * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.runtime.module.artifact.classloader; import static java.lang.System.lineSeparator; import static java.util.Arrays.asList; import static java.util.Collections.emptyList; import static java.util.Collections.singletonList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mule.runtime.core.api.config.MuleProperties.MULE_LOG_VERBOSE_CLASSLOADING; import static org.mule.runtime.module.artifact.classloader.EnumerationMatcher.equalTo; import org.mule.runtime.module.artifact.classloader.exception.NotExportedClassException; import org.mule.tck.junit4.AbstractMuleTestCase; import org.mule.tck.junit4.rule.SystemProperty; import java.io.IOException; import java.net.URL; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.List; import org.hamcrest.CoreMatchers; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) public class FilteringArtifactClassLoaderTestCase extends AbstractMuleTestCase { public static final String CLASS_NAME = "java.lang.Object"; public static final String RESOURCE_NAME = "dummy.txt"; private static final String SERVICE_INTERFACE_NAME = "org.foo.Service"; public static final String SERVICE_RESOURCE_NAME = "META-INF/services/" + SERVICE_INTERFACE_NAME; @Rule public ExpectedException expected = ExpectedException.none(); public boolean verboseClassloadingLog; @Rule public SystemProperty verboseClassloading; protected FilteringArtifactClassLoader filteringArtifactClassLoader; protected final ClassLoaderFilter filter = mock(ClassLoaderFilter.class); protected final ArtifactClassLoader artifactClassLoader = mock(ArtifactClassLoader.class); @Parameters(name = "verbose: {0}") public static Collection<Object[]> params() { return asList(new Object[][] {{true}, {false}}); } public FilteringArtifactClassLoaderTestCase(boolean verboseClassloadingLog) { this.verboseClassloadingLog = verboseClassloadingLog; verboseClassloading = new SystemProperty(MULE_LOG_VERBOSE_CLASSLOADING, Boolean.toString(verboseClassloadingLog)); } @Before public void before() { when(artifactClassLoader.getArtifactId()).thenReturn("mockArtifact"); } @Test public void throwClassNotFoundErrorWhenClassIsNotExported() throws ClassNotFoundException { expected.expect(NotExportedClassException.class); if (verboseClassloadingLog) { expected.expectMessage(is("Class '" + CLASS_NAME + "' not found in classloader for artifact 'mockArtifact'." + lineSeparator() + filter.toString())); } else { expected.expectMessage(is("Class '" + CLASS_NAME + "' not found in classloader for artifact 'mockArtifact'.")); } when(filter.exportsClass(CLASS_NAME)).thenReturn(false); filteringArtifactClassLoader = doCreateClassLoader(emptyList()); filteringArtifactClassLoader.loadClass(CLASS_NAME); } protected FilteringArtifactClassLoader doCreateClassLoader(List<ExportedService> exportedServices) { return new FilteringArtifactClassLoader(artifactClassLoader, filter, exportedServices); } @Test public void loadsExportedClass() throws ClassNotFoundException { TestClassLoader classLoader = new TestClassLoader(); Class expectedClass = this.getClass(); classLoader.addClass(CLASS_NAME, expectedClass); when(filter.exportsClass(CLASS_NAME)).thenReturn(true); when(artifactClassLoader.getClassLoader()).thenReturn(classLoader); filteringArtifactClassLoader = doCreateClassLoader(emptyList()); Class<?> aClass = filteringArtifactClassLoader.loadClass(CLASS_NAME); assertThat(aClass, equalTo(expectedClass)); } @Test public void filtersResourceWhenNotExported() throws ClassNotFoundException { when(filter.exportsClass(RESOURCE_NAME)).thenReturn(false); filteringArtifactClassLoader = doCreateClassLoader(emptyList()); URL resource = filteringArtifactClassLoader.getResource(RESOURCE_NAME); assertThat(resource, CoreMatchers.equalTo(null)); } @Test public void loadsExportedResource() throws ClassNotFoundException, IOException { URL expectedResource = new URL("file:///app.txt"); when(filter.exportsResource(RESOURCE_NAME)).thenReturn(true); when(artifactClassLoader.findResource(RESOURCE_NAME)).thenReturn(expectedResource); filteringArtifactClassLoader = doCreateClassLoader(emptyList()); URL resource = filteringArtifactClassLoader.getResource(RESOURCE_NAME); assertThat(resource, equalTo(expectedResource)); } @Test public void loadsExportedService() throws ClassNotFoundException, IOException { URL expectedResource = new URL("file:///app.txt"); filteringArtifactClassLoader = doCreateClassLoader(singletonList(new ExportedService(SERVICE_INTERFACE_NAME, expectedResource))); URL resource = filteringArtifactClassLoader.getResource(SERVICE_RESOURCE_NAME); assertThat(resource, equalTo(expectedResource)); verify(filter, never()).exportsResource(SERVICE_RESOURCE_NAME); verify(artifactClassLoader, never()).findResource(SERVICE_RESOURCE_NAME); } @Test public void filtersResources() throws Exception { TestClassLoader classLoader = new TestClassLoader(); URL blockedResource = new URL("file:///app.txt"); classLoader.addResource(RESOURCE_NAME, blockedResource); when(filter.exportsResource(RESOURCE_NAME)).thenReturn(false); when(artifactClassLoader.getClassLoader()).thenReturn(classLoader); filteringArtifactClassLoader = doCreateClassLoader(emptyList()); Enumeration<URL> resources = filteringArtifactClassLoader.getResources(RESOURCE_NAME); assertThat(resources, equalTo(Collections.EMPTY_LIST)); } @Test public void getsExportedResources() throws Exception { URL resource = new URL("file:/app.txt"); when(filter.exportsResource(RESOURCE_NAME)).thenReturn(true); when(artifactClassLoader.findResources(RESOURCE_NAME)).thenReturn(new EnumerationAdapter<>(Collections.singleton(resource))); filteringArtifactClassLoader = doCreateClassLoader(emptyList()); Enumeration<URL> resources = filteringArtifactClassLoader.getResources(RESOURCE_NAME); assertThat(resources, equalTo(Collections.singletonList(resource))); } @Test public void loadsExportedServices() throws ClassNotFoundException, IOException { URL expectedResource = new URL("file:///app.txt"); filteringArtifactClassLoader = doCreateClassLoader(singletonList(new ExportedService(SERVICE_INTERFACE_NAME, expectedResource))); URL resource = filteringArtifactClassLoader.getResource(SERVICE_RESOURCE_NAME); Enumeration<URL> resources = filteringArtifactClassLoader.getResources(SERVICE_RESOURCE_NAME); assertThat(resources, equalTo(Collections.singletonList(resource))); verify(filter, never()).exportsResource(SERVICE_RESOURCE_NAME); verify(artifactClassLoader, never()).findResources(SERVICE_RESOURCE_NAME); } @Test public void returnsCorrectClassLoader() throws Exception { filteringArtifactClassLoader = doCreateClassLoader(emptyList()); final ClassLoader classLoader = filteringArtifactClassLoader.getClassLoader(); assertThat(classLoader, is(filteringArtifactClassLoader)); } @Test public void doesNotDisposesFilteredClassLoader() throws Exception { filteringArtifactClassLoader = doCreateClassLoader(emptyList()); filteringArtifactClassLoader.dispose(); verify(artifactClassLoader, never()).dispose(); } }