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;
|
<R, W, C> SimpleWorkdirFactory.ParentAndClone<R, W> getWorkdir(CreateWorkdirContext<R, W, C> context) throws Exception;
|
||||||
|
|
||||||
void contextClosed(CreateWorkdirContext<?, ?, ?> createWorkdirContext, File workdir) 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 {
|
private <R, W> SimpleWorkdirFactory.ParentAndClone<R, W> createNewWorkdir(CreateWorkdirContext<R, W, ?> createWorkdirContext) throws IOException {
|
||||||
String id = createWorkdirContext.getScmRepository().getId();
|
|
||||||
Stopwatch stopwatch = Stopwatch.createStarted();
|
Stopwatch stopwatch = Stopwatch.createStarted();
|
||||||
File newWorkdir = workdirProvider.createNewWorkdir();
|
File newWorkdir = workdirProvider.createNewWorkdir();
|
||||||
workdirs.put(id, newWorkdir);
|
|
||||||
SimpleWorkdirFactory.ParentAndClone<R, W> parentAndClone = createWorkdirContext.getInitializer().initialize(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());
|
LOG.debug("initialized new workdir for {} in path {} in {}", createWorkdirContext.getScmRepository().getNamespaceAndName(), newWorkdir, stopwatch.stop());
|
||||||
return parentAndClone;
|
return parentAndClone;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void contextClosed(CreateWorkdirContext<?, ?, ?> createWorkdirContext, File workdir) throws IOException {
|
public void contextClosed(CreateWorkdirContext<?, ?, ?> createWorkdirContext, File workdir) {
|
||||||
String id = createWorkdirContext.getScmRepository().getId();
|
String id = createWorkdirContext.getScmRepository().getId();
|
||||||
File putResult = workdirs.putIfAbsent(id, workdir);
|
File putResult = workdirs.putIfAbsent(id, workdir);
|
||||||
if (putResult != null && putResult != workdir) {
|
if (putResult != null && putResult != workdir) {
|
||||||
@@ -85,7 +83,15 @@ public class CachingAllWorkdirProvider implements CacheSupportingWorkdirProvider
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void deleteWorkdir(File existingWorkdir) throws IOException {
|
@Override
|
||||||
IOUtil.delete(existingWorkdir, true);
|
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 {
|
public void contextClosed(CreateWorkdirContext<?, ?, ?> createWorkdirContext, File workdir) throws IOException {
|
||||||
IOUtil.delete(workdir, true);
|
IOUtil.delete(workdir, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,13 +26,17 @@ package sonia.scm.repository.util;
|
|||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sonia.scm.plugin.Extension;
|
||||||
import sonia.scm.repository.InternalRepositoryException;
|
import sonia.scm.repository.InternalRepositoryException;
|
||||||
import sonia.scm.repository.Repository;
|
import sonia.scm.repository.Repository;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContextEvent;
|
||||||
|
import javax.servlet.ServletContextListener;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
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);
|
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
|
@FunctionalInterface
|
||||||
public interface WorkdirInitializer<R, W> {
|
public interface WorkdirInitializer<R, W> {
|
||||||
ParentAndClone<R, W> initialize(File target) throws IOException;
|
ParentAndClone<R, W> initialize(File target) throws IOException;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
* SOFTWARE.
|
* SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package sonia.scm.util;
|
package sonia.scm.util;
|
||||||
|
|
||||||
//~--- non-JDK imports --------------------------------------------------------
|
//~--- non-JDK imports --------------------------------------------------------
|
||||||
@@ -358,6 +358,15 @@ public final class IOUtil
|
|||||||
delete(file, false);
|
delete(file, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void deleteSilently(File file)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
delete(file, true);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// silent delete throws no exception
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method description
|
* 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.repository.Repository;
|
||||||
import sonia.scm.util.IOUtil;
|
import sonia.scm.util.IOUtil;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContextEvent;
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -72,6 +73,10 @@ public class SimpleWorkdirFactoryTest {
|
|||||||
IOUtil.delete(workdir);
|
IOUtil.delete(workdir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void shutdown() {
|
||||||
|
}
|
||||||
};
|
};
|
||||||
simpleWorkdirFactory = new SimpleWorkdirFactory<Closeable, Closeable, Context>(configurableTestWorkdirProvider) {
|
simpleWorkdirFactory = new SimpleWorkdirFactory<Closeable, Closeable, Context>(configurableTestWorkdirProvider) {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import sonia.scm.web.HgRepositoryEnvironmentBuilder;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
import javax.servlet.ServletContextEvent;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import sonia.scm.repository.util.CacheSupportingWorkdirProvider;
|
|||||||
import sonia.scm.repository.util.SimpleWorkdirFactory;
|
import sonia.scm.repository.util.SimpleWorkdirFactory;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
import javax.servlet.ServletContextEvent;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user