/* * 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.launcher.log4j2; import static org.apache.commons.lang.StringUtils.EMPTY; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.sameInstance; import static org.hamcrest.core.IsInstanceOf.instanceOf; import static org.hamcrest.core.IsNot.not; import static org.junit.Assert.assertThat; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.RETURNS_DEEP_STUBS; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.mule.runtime.core.api.config.MuleProperties.MULE_LOG_CONTEXT_DISPOSE_DELAY_MILLIS; import static org.mule.runtime.module.launcher.log4j2.LoggerContextReaperThreadFactory.THREAD_NAME; import static org.mule.tck.MuleTestUtils.getRunningThreadByName; import org.mule.runtime.api.util.Reference; import org.mule.runtime.deployment.model.api.application.ApplicationDescriptor; import org.mule.runtime.module.artifact.classloader.ArtifactClassLoader; import org.mule.runtime.module.artifact.classloader.ClassLoaderLookupPolicy; import org.mule.runtime.module.artifact.classloader.MuleArtifactClassLoader; import org.mule.runtime.module.artifact.classloader.RegionClassLoader; import org.mule.runtime.module.artifact.classloader.ShutdownListener; import org.mule.runtime.module.reboot.MuleContainerBootstrapUtils; import org.mule.tck.junit4.AbstractMuleTestCase; import org.mule.tck.junit4.rule.SystemProperty; import org.mule.tck.probe.JUnitProbe; import org.mule.tck.probe.PollingProber; import org.mule.tck.probe.Probe; import org.mule.tck.size.SmallTest; import java.io.File; import java.net.URL; import java.net.URLClassLoader; import org.apache.logging.log4j.core.LifeCycle; import org.apache.logging.log4j.core.LoggerContext; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; @SmallTest @RunWith(MockitoJUnitRunner.class) public class ArtifactAwareContextSelectorTestCase extends AbstractMuleTestCase { private static final File CONFIG_LOCATION = new File("my/local/log4j2.xml"); private static final int PROBER_TIMEOUT = 5000; private static final int PROBER_FREQ = 500; @Rule public SystemProperty disposeDelay = new SystemProperty(MULE_LOG_CONTEXT_DISPOSE_DELAY_MILLIS, "200"); private ArtifactAwareContextSelector selector; @Mock(answer = Answers.RETURNS_DEEP_STUBS) private RegionClassLoader regionClassLoader; @Before public void before() throws Exception { selector = new ArtifactAwareContextSelector(); when(regionClassLoader.getArtifactId()).thenReturn(getClass().getName()); when(regionClassLoader.findLocalResource("log4j2.xml")).thenReturn(CONFIG_LOCATION.toURI().toURL()); } @Test public void classLoaderToContext() { MuleLoggerContext context = (MuleLoggerContext) selector.getContext(EMPTY, regionClassLoader, true); assertThat(context, is(sameInstance(selector.getContext(EMPTY, regionClassLoader, true)))); regionClassLoader = mock(RegionClassLoader.class, RETURNS_DEEP_STUBS); when(regionClassLoader.getArtifactId()).thenReturn(getClass().getName()); assertThat(context, not(sameInstance(selector.getContext(EMPTY, regionClassLoader, true)))); } @Test public void shutdownListener() { MuleLoggerContext context = getContext(); ArgumentCaptor<ShutdownListener> captor = ArgumentCaptor.forClass(ShutdownListener.class); verify(regionClassLoader).addShutdownListener(captor.capture()); ShutdownListener listener = captor.getValue(); assertThat(listener, notNullValue()); assertThat(context, is(selector.getContext(EMPTY, regionClassLoader, true))); listener.execute(); assertStopped(context); } @Test public void dispose() throws Exception { MuleLoggerContext context = getContext(); selector.dispose(); assertStopped(context); } @Test public void returnsMuleLoggerContext() { LoggerContext ctx = selector.getContext("", regionClassLoader, true); assertThat(ctx, instanceOf(MuleLoggerContext.class)); assertConfigurationLocation(ctx); } @Test public void defaultToConfWhenNoConfigFound() { when(regionClassLoader.findLocalResource(anyString())).thenReturn(null); File expected = new File(MuleContainerBootstrapUtils.getMuleHome(), "conf"); expected = new File(expected, "log4j2.xml"); LoggerContext ctx = selector.getContext("", regionClassLoader, true); assertThat(ctx.getConfigLocation(), equalTo(expected.toURI())); } @Test public void usesLoggerContextReaperThread() { assertReaperThreadNotRunning(); MuleLoggerContext context = getContext(); selector.removeContext(context); Thread thread = getReaperThread(); assertThat(thread, is(notNullValue())); } @Test public void returnsMuleLoggerContextForArtifactClassLoaderChild() { ClassLoader childClassLoader = new URLClassLoader(new URL[0], regionClassLoader); LoggerContext parentCtx = selector.getContext("", regionClassLoader, true); LoggerContext childCtx = selector.getContext("", childClassLoader, true); assertThat(childCtx, instanceOf(MuleLoggerContext.class)); assertThat(childCtx, sameInstance(parentCtx)); } @Test public void returnsMuleLoggerContextForInternalArtifactClassLoader() { ArtifactClassLoader serviceClassLoader = new MuleArtifactClassLoader("test", new ApplicationDescriptor("test"), new URL[0], this.getClass().getClassLoader(), mock(ClassLoaderLookupPolicy.class)); LoggerContext systemContext = selector.getContext("", this.getClass().getClassLoader(), true); LoggerContext serviceCtx = selector.getContext("", serviceClassLoader.getClassLoader(), true); assertThat(serviceCtx, instanceOf(MuleLoggerContext.class)); assertThat(serviceCtx, sameInstance(systemContext)); } private void assertReaperThreadNotRunning() { PollingProber prober = new PollingProber(PROBER_TIMEOUT, PROBER_FREQ); prober.check(new Probe() { @Override public boolean isSatisfied() { return getReaperThread() == null; } @Override public String describeFailure() { return "Reaper thread exists from previous test and did not died"; } }); } private Thread getReaperThread() { return getRunningThreadByName(THREAD_NAME); } private MuleLoggerContext getContext() { return (MuleLoggerContext) selector.getContext("", regionClassLoader, true); } private void assertConfigurationLocation(LoggerContext ctx) { assertThat(ctx.getConfigLocation(), equalTo(CONFIG_LOCATION.toURI())); } private void assertStopped(final MuleLoggerContext context) { final Reference<Boolean> contextWasAccessibleDuringShutdown = new Reference<>(true); PollingProber pollingProber = new PollingProber(1000, 10); pollingProber.check(new JUnitProbe() { @Override protected boolean test() throws Exception { if (context.getState().equals(LifeCycle.State.STOPPED)) { return true; } else { LoggerContext currentContext = getContext(); if (currentContext != null && currentContext != context) { contextWasAccessibleDuringShutdown.set(false); } return false; } } @Override public String describeFailure() { return "context was not stopped"; } }); assertThat(context, not(getContext())); assertThat(contextWasAccessibleDuringShutdown.get(), is(true)); } }