mirror of
https://github.com/scm-manager/scm-manager.git
synced 2026-07-04 13:49:14 +02:00
use ClassLoaderLeakPreventor to reduce ClassLoaderLeaks of plugins
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
package sonia.scm.boot;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventor;
|
||||
import se.jiderhamn.classloader.leak.prevention.ClassLoaderLeakPreventorFactory;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class ClassLoaderLifeCycleTest {
|
||||
|
||||
@Mock
|
||||
private ClassLoaderLeakPreventorFactory classLoaderLeakPreventorFactory;
|
||||
|
||||
@Mock
|
||||
private ClassLoaderLeakPreventor classLoaderLeakPreventor;
|
||||
|
||||
@Test
|
||||
void shouldThrowIllegalStateExceptionWithoutInit() {
|
||||
ClassLoaderLifeCycle lifeCycle = ClassLoaderLifeCycle.create();
|
||||
assertThrows(IllegalStateException.class, lifeCycle::getBootstrapClassLoader);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldThrowIllegalStateExceptionAfterShutdown() {
|
||||
ClassLoaderLifeCycle lifeCycle = createMockedLifeCycle();
|
||||
lifeCycle.init();
|
||||
|
||||
lifeCycle.shutdown();
|
||||
assertThrows(IllegalStateException.class, lifeCycle::getBootstrapClassLoader);
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateBootstrapClassLoaderOnInit() {
|
||||
ClassLoaderLifeCycle lifeCycle = ClassLoaderLifeCycle.create();
|
||||
lifeCycle.init();
|
||||
|
||||
assertThat(lifeCycle.getBootstrapClassLoader()).isNotNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCallTheLeakPreventor() {
|
||||
ClassLoaderLifeCycle lifeCycle = createMockedLifeCycle();
|
||||
|
||||
lifeCycle.init();
|
||||
verify(classLoaderLeakPreventor, times(2)).runPreClassLoaderInitiators();
|
||||
|
||||
lifeCycle.createChildFirstPluginClassLoader(new URL[0], null, "a");
|
||||
lifeCycle.createPluginClassLoader(new URL[0], null, "b");
|
||||
verify(classLoaderLeakPreventor, times(4)).runPreClassLoaderInitiators();
|
||||
|
||||
lifeCycle.shutdown();
|
||||
verify(classLoaderLeakPreventor, times(4)).runCleanUps();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCloseCloseableClassLoaders() throws IOException {
|
||||
// we use URLClassLoader, because we must be sure that the classloader is closable
|
||||
URLClassLoader webappClassLoader = spy(new URLClassLoader(new URL[0], Thread.currentThread().getContextClassLoader()));
|
||||
|
||||
ClassLoaderLifeCycle lifeCycle = createMockedLifeCycle(webappClassLoader);
|
||||
lifeCycle.setClassLoaderAppendListener(c -> spy(c));
|
||||
lifeCycle.init();
|
||||
|
||||
ClassLoader pluginA = lifeCycle.createChildFirstPluginClassLoader(new URL[0], null, "a");
|
||||
ClassLoader pluginB = lifeCycle.createPluginClassLoader(new URL[0], null, "b");
|
||||
|
||||
lifeCycle.shutdown();
|
||||
|
||||
closed(pluginB);
|
||||
closed(pluginA);
|
||||
|
||||
neverClosed(webappClassLoader);
|
||||
}
|
||||
|
||||
private void neverClosed(Object object) throws IOException {
|
||||
Closeable closeable = closeable(object);
|
||||
verify(closeable, never()).close();
|
||||
}
|
||||
|
||||
private void closed(Object object) throws IOException {
|
||||
Closeable closeable = closeable(object);
|
||||
verify(closeable).close();
|
||||
}
|
||||
|
||||
private Closeable closeable(Object object) {
|
||||
assertThat(object).isInstanceOf(Closeable.class);
|
||||
return (Closeable) object;
|
||||
}
|
||||
|
||||
private ClassLoaderLifeCycle createMockedLifeCycle() {
|
||||
return createMockedLifeCycle(Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
|
||||
private ClassLoaderLifeCycle createMockedLifeCycle(ClassLoader classLoader) {
|
||||
when(classLoaderLeakPreventorFactory.newLeakPreventor(any(ClassLoader.class))).thenReturn(classLoaderLeakPreventor);
|
||||
return new ClassLoaderLifeCycle(classLoader, classLoaderLeakPreventorFactory);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import sonia.scm.boot.ClassLoaderLifeCycle;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
@@ -288,7 +289,7 @@ public class PluginProcessorTest
|
||||
public void setUp() throws IOException
|
||||
{
|
||||
pluginDirectory = temp.newFolder();
|
||||
processor = new PluginProcessor(pluginDirectory.toPath());
|
||||
processor = new PluginProcessor(ClassLoaderLifeCycle.create(), pluginDirectory.toPath());
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user