mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-02 11:35:57 +01:00
Add shutdown
This commit is contained in:
@@ -30,4 +30,6 @@ public interface CacheSupportingWorkdirProvider {
|
||||
<R, W, C> SimpleWorkdirFactory.ParentAndClone<R, W> getWorkdir(CreateWorkdirContext<R, W, C> context) throws Exception;
|
||||
|
||||
void contextClosed(CreateWorkdirContext<?, ?, ?> createWorkdirContext, File workdir) throws Exception;
|
||||
|
||||
void shutdown();
|
||||
}
|
||||
|
||||
@@ -67,17 +67,15 @@ public class CachingAllWorkdirProvider implements CacheSupportingWorkdirProvider
|
||||
}
|
||||
|
||||
private <R, W> SimpleWorkdirFactory.ParentAndClone<R, W> createNewWorkdir(CreateWorkdirContext<R, W, ?> createWorkdirContext) throws IOException {
|
||||
String id = createWorkdirContext.getScmRepository().getId();
|
||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||
File newWorkdir = workdirProvider.createNewWorkdir();
|
||||
workdirs.put(id, newWorkdir);
|
||||
SimpleWorkdirFactory.ParentAndClone<R, W> parentAndClone = createWorkdirContext.getInitializer().initialize(newWorkdir);
|
||||
LOG.debug("initialized new workdir for {} in path {} in {}", createWorkdirContext.getScmRepository().getNamespaceAndName(), newWorkdir, stopwatch.stop());
|
||||
return parentAndClone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextClosed(CreateWorkdirContext<?, ?, ?> createWorkdirContext, File workdir) throws IOException {
|
||||
public void contextClosed(CreateWorkdirContext<?, ?, ?> createWorkdirContext, File workdir) {
|
||||
String id = createWorkdirContext.getScmRepository().getId();
|
||||
File putResult = workdirs.putIfAbsent(id, workdir);
|
||||
if (putResult != null && putResult != workdir) {
|
||||
@@ -85,7 +83,15 @@ public class CachingAllWorkdirProvider implements CacheSupportingWorkdirProvider
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteWorkdir(File existingWorkdir) throws IOException {
|
||||
IOUtil.delete(existingWorkdir, true);
|
||||
@Override
|
||||
public void shutdown() {
|
||||
workdirs.values().parallelStream().forEach(this::deleteWorkdir);
|
||||
workdirs.clear();
|
||||
}
|
||||
|
||||
private void deleteWorkdir(File existingWorkdir) {
|
||||
if (existingWorkdir.exists()) {
|
||||
IOUtil.deleteSilently(existingWorkdir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,4 +48,8 @@ public class NoneCachingWorkdirProvider implements CacheSupportingWorkdirProvide
|
||||
public void contextClosed(CreateWorkdirContext<?, ?, ?> createWorkdirContext, File workdir) throws IOException {
|
||||
IOUtil.delete(workdir, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,13 +26,17 @@ package sonia.scm.repository.util;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.plugin.Extension;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class SimpleWorkdirFactory<R, W, C> implements WorkdirFactory<R, W, C> {
|
||||
@Extension
|
||||
public abstract class SimpleWorkdirFactory<R, W, C> implements WorkdirFactory<R, W, C>, ServletContextListener {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(SimpleWorkdirFactory.class);
|
||||
|
||||
@@ -79,6 +83,16 @@ public abstract class SimpleWorkdirFactory<R, W, C> implements WorkdirFactory<R,
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce) {
|
||||
workdirProvider.shutdown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce) {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface WorkdirInitializer<R, W> {
|
||||
ParentAndClone<R, W> initialize(File target) throws IOException;
|
||||
|
||||
@@ -358,6 +358,15 @@ public final class IOUtil
|
||||
delete(file, false);
|
||||
}
|
||||
|
||||
public static void deleteSilently(File file)
|
||||
{
|
||||
try {
|
||||
delete(file, true);
|
||||
} catch (IOException e) {
|
||||
// silent delete throws no exception
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.repository.util;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junitpioneer.jupiter.TempDirectory;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import sonia.scm.repository.Repository;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith({MockitoExtension.class, TempDirectory.class})
|
||||
class CachingAllWorkdirProviderTest {
|
||||
|
||||
private static final Repository REPOSITORY = new Repository("1", "git", "space", "X");
|
||||
|
||||
@Mock
|
||||
WorkdirProvider workdirProvider;
|
||||
@InjectMocks
|
||||
CachingAllWorkdirProvider cachingAllWorkdirProvider;
|
||||
|
||||
@Mock
|
||||
CreateWorkdirContext<Object, Path, Path> createWorkdirContext;
|
||||
@Mock
|
||||
SimpleWorkdirFactory.WorkdirInitializer<Object, Path> initializer;
|
||||
@Mock
|
||||
SimpleWorkdirFactory.WorkdirReclaimer<Object, Path> reclaimer;
|
||||
|
||||
@BeforeEach
|
||||
void initContext() throws IOException, SimpleWorkdirFactory.ReclaimFailedException {
|
||||
lenient().when(createWorkdirContext.getInitializer()).thenReturn(initializer);
|
||||
lenient().when(createWorkdirContext.getReclaimer()).thenReturn(reclaimer);
|
||||
|
||||
lenient().when(initializer.initialize(any()))
|
||||
.thenAnswer(invocationOnMock -> new SimpleWorkdirFactory.ParentAndClone<Object, Object>(null, null, invocationOnMock.getArgument(0, File.class)));
|
||||
lenient().when(reclaimer.reclaim(any()))
|
||||
.thenAnswer(invocationOnMock -> new SimpleWorkdirFactory.ParentAndClone<Object, Object>(null, null, invocationOnMock.getArgument(0, File.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateNewWorkdirForTheFirstRequest(@TempDirectory.TempDir Path temp) throws IOException {
|
||||
when(createWorkdirContext.getScmRepository()).thenReturn(REPOSITORY);
|
||||
when(workdirProvider.createNewWorkdir()).thenReturn(temp.toFile());
|
||||
|
||||
SimpleWorkdirFactory.ParentAndClone<?, ?> workdir = cachingAllWorkdirProvider.getWorkdir(createWorkdirContext);
|
||||
|
||||
verify(initializer).initialize(temp.toFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCreateWorkdirOnlyOnceForTheSameRepository(@TempDirectory.TempDir Path temp) throws IOException, SimpleWorkdirFactory.ReclaimFailedException {
|
||||
when(createWorkdirContext.getScmRepository()).thenReturn(REPOSITORY);
|
||||
when(workdirProvider.createNewWorkdir()).thenReturn(temp.toFile());
|
||||
|
||||
SimpleWorkdirFactory.ParentAndClone<?, ?> firstWorkdir = cachingAllWorkdirProvider.getWorkdir(createWorkdirContext);
|
||||
cachingAllWorkdirProvider.contextClosed(createWorkdirContext, firstWorkdir.getDirectory());
|
||||
SimpleWorkdirFactory.ParentAndClone<?, ?> secondWorkdir = cachingAllWorkdirProvider.getWorkdir(createWorkdirContext);
|
||||
|
||||
verify(initializer).initialize(temp.toFile());
|
||||
verify(reclaimer).reclaim(temp.toFile());
|
||||
assertThat(secondWorkdir.getDirectory()).isEqualTo(temp.toFile());
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCacheOnlyOneWorkdirForRepository(@TempDirectory.TempDir Path temp) throws IOException, SimpleWorkdirFactory.ReclaimFailedException {
|
||||
when(createWorkdirContext.getScmRepository()).thenReturn(REPOSITORY);
|
||||
File firstDirectory = temp.resolve("first").toFile();
|
||||
firstDirectory.mkdirs();
|
||||
File secondDirectory = temp.resolve("second").toFile();
|
||||
secondDirectory.mkdirs();
|
||||
when(workdirProvider.createNewWorkdir()).thenReturn(
|
||||
firstDirectory,
|
||||
secondDirectory);
|
||||
|
||||
SimpleWorkdirFactory.ParentAndClone<?, ?> firstWorkdir = cachingAllWorkdirProvider.getWorkdir(createWorkdirContext);
|
||||
SimpleWorkdirFactory.ParentAndClone<?, ?> secondWorkdir = cachingAllWorkdirProvider.getWorkdir(createWorkdirContext);
|
||||
cachingAllWorkdirProvider.contextClosed(createWorkdirContext, firstWorkdir.getDirectory());
|
||||
cachingAllWorkdirProvider.contextClosed(createWorkdirContext, secondWorkdir.getDirectory());
|
||||
|
||||
verify(reclaimer, never()).reclaim(any());
|
||||
verify(initializer).initialize(firstDirectory);
|
||||
verify(initializer).initialize(secondDirectory);
|
||||
assertThat(firstWorkdir.getDirectory()).isNotEqualTo(secondWorkdir.getDirectory());
|
||||
assertThat(firstWorkdir.getDirectory()).exists();
|
||||
assertThat(secondWorkdir.getDirectory()).doesNotExist();
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import org.junit.rules.TemporaryFolder;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.util.IOUtil;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import java.io.Closeable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
@@ -72,6 +73,10 @@ public class SimpleWorkdirFactoryTest {
|
||||
IOUtil.delete(workdir);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
};
|
||||
simpleWorkdirFactory = new SimpleWorkdirFactory<Closeable, Closeable, Context>(configurableTestWorkdirProvider) {
|
||||
@Override
|
||||
|
||||
@@ -36,6 +36,7 @@ import sonia.scm.web.HgRepositoryEnvironmentBuilder;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -40,6 +40,7 @@ import sonia.scm.repository.util.CacheSupportingWorkdirProvider;
|
||||
import sonia.scm.repository.util.SimpleWorkdirFactory;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user