Introduce cache layer for workdirs

This commit is contained in:
René Pfeuffer
2020-04-13 12:22:13 +02:00
parent c92119f5eb
commit 5b034f8d02
21 changed files with 370 additions and 47 deletions

View File

@@ -0,0 +1,42 @@
/*
* 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 sonia.scm.repository.Repository;
import java.io.File;
import java.io.IOException;
public interface CacheSupportingWorkdirProvider {
<R, W, C> SimpleWorkdirFactory.ParentAndClone<R, W> getWorkdir(
Repository scmRepository,
String requestedBranch,
C context,
SimpleWorkdirFactory.WorkdirInitializer<R, W> initializer,
SimpleWorkdirFactory.WorkdirReclaimer<R, W> reclaimer
) throws IOException;
boolean cache(Repository repository, File target) throws IOException;
}

View File

@@ -0,0 +1,72 @@
/*
* 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 sonia.scm.repository.Repository;
import sonia.scm.util.IOUtil;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
public class CachingAllWorkdirProvider implements CacheSupportingWorkdirProvider {
private final Map<String, File> workdirs = new HashMap<>();
private final WorkdirProvider workdirProvider;
@Inject
public CachingAllWorkdirProvider(WorkdirProvider workdirProvider) {
this.workdirProvider = workdirProvider;
}
@Override
public <R, W, C> SimpleWorkdirFactory.ParentAndClone<R, W> getWorkdir(Repository scmRepository, String requestedBranch, C context, SimpleWorkdirFactory.WorkdirInitializer<R, W> initializer, SimpleWorkdirFactory.WorkdirReclaimer<R, W> reclaimer) throws IOException {
String id = scmRepository.getId();
if (workdirs.containsKey(id)) {
File existingWorkdir = workdirs.get(id);
try {
return reclaimer.reclaim(existingWorkdir);
} catch (SimpleWorkdirFactory.ReclaimFailedException e) {
workdirs.remove(id);
IOUtil.delete(existingWorkdir, true);
}
}
return createNewWorkdir(initializer, id);
}
public <R, W> SimpleWorkdirFactory.ParentAndClone<R, W> createNewWorkdir(SimpleWorkdirFactory.WorkdirInitializer<R, W> initializer, String id) throws IOException {
File newWorkdir = workdirProvider.createNewWorkdir();
workdirs.put(id, newWorkdir);
return initializer.initialize(newWorkdir);
}
@Override
public boolean cache(Repository repository, File target) {
return true;
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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 sonia.scm.repository.Repository;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
public class NoneCachingWorkdirProvider implements CacheSupportingWorkdirProvider {
private final WorkdirProvider workdirProvider;
@Inject
public NoneCachingWorkdirProvider(WorkdirProvider workdirProvider) {
this.workdirProvider = workdirProvider;
}
@Override
public <R, W, C> SimpleWorkdirFactory.ParentAndClone<R, W> getWorkdir(Repository scmRepository, String requestedBranch, C context, SimpleWorkdirFactory.WorkdirInitializer<R, W> initializer, SimpleWorkdirFactory.WorkdirReclaimer<R, W> reclaimer) throws IOException {
return initializer.initialize(workdirProvider.createNewWorkdir());
}
@Override
public boolean cache(Repository repository, File target) {
return false;
}
}

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.repository.util;
import org.slf4j.Logger;
@@ -36,23 +36,45 @@ public abstract class SimpleWorkdirFactory<R, W, C> implements WorkdirFactory<R,
private static final Logger logger = LoggerFactory.getLogger(SimpleWorkdirFactory.class);
private final WorkdirProvider workdirProvider;
private final CacheSupportingWorkdirProvider workdirProvider;
public SimpleWorkdirFactory(WorkdirProvider workdirProvider) {
public SimpleWorkdirFactory(CacheSupportingWorkdirProvider workdirProvider) {
this.workdirProvider = workdirProvider;
}
@Override
public WorkingCopy<R, W> createWorkingCopy(C context, String initialBranch) {
try {
File directory = workdirProvider.createNewWorkdir();
ParentAndClone<R, W> parentAndClone = cloneRepository(context, directory, initialBranch);
return new WorkingCopy<>(parentAndClone.getClone(), parentAndClone.getParent(), this::closeWorkdir, this::closeCentral, directory);
ParentAndClone<R, W> parentAndClone = workdirProvider.getWorkdir(
getScmRepository(context),
initialBranch,
context,
newFolder -> cloneRepository(context, newFolder, initialBranch),
cachedFolder -> reclaimRepository(context, cachedFolder, initialBranch)
);
return new WorkingCopy<R, W>(parentAndClone.getClone(), parentAndClone.getParent(), this::closeWorkdir, this::closeCentral, parentAndClone.getDirectory()) {
@Override
public void delete() throws IOException {
if (!workdirProvider.cache(getScmRepository(context), getDirectory())) {
super.delete();
}
}
};
} catch (IOException e) {
throw new InternalRepositoryException(getScmRepository(context), "could not clone repository in temporary directory", e);
}
}
@FunctionalInterface
public interface WorkdirInitializer<R, W> {
ParentAndClone<R, W> initialize(File target) throws IOException;
}
@FunctionalInterface
public interface WorkdirReclaimer<R, W> {
ParentAndClone<R, W> reclaim(File target) throws IOException, ReclaimFailedException;
}
protected abstract Repository getScmRepository(C context);
@SuppressWarnings("squid:S00112")
@@ -64,6 +86,8 @@ public abstract class SimpleWorkdirFactory<R, W, C> implements WorkdirFactory<R,
protected abstract ParentAndClone<R, W> cloneRepository(C context, File target, String initialBranch) throws IOException;
protected abstract ParentAndClone<R, W> reclaimRepository(C context, File target, String initialBranch) throws IOException;
private void closeCentral(R repository) {
try {
closeRepository(repository);
@@ -83,10 +107,12 @@ public abstract class SimpleWorkdirFactory<R, W, C> implements WorkdirFactory<R,
protected static class ParentAndClone<R, W> {
private final R parent;
private final W clone;
private final File directory;
public ParentAndClone(R parent, W clone) {
public ParentAndClone(R parent, W clone, File directory) {
this.parent = parent;
this.clone = clone;
this.directory = directory;
}
public R getParent() {
@@ -96,5 +122,12 @@ public abstract class SimpleWorkdirFactory<R, W, C> implements WorkdirFactory<R,
public W getClone() {
return clone;
}
public File getDirectory() {
return directory;
}
}
public static class ReclaimFailedException extends Exception {
}
}

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.repository.util;
import org.slf4j.Logger;
@@ -67,9 +67,13 @@ public class WorkingCopy<R, W> implements AutoCloseable {
try {
cleanupWorkdir.accept(workingRepository);
cleanupCentral.accept(centralRepository);
IOUtil.delete(directory);
delete();
} catch (IOException e) {
LOG.warn("could not delete temporary workdir '{}'", directory, e);
}
}
void delete() throws IOException {
IOUtil.delete(directory);
}
}

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.repository.util;
@@ -52,10 +52,25 @@ public class SimpleWorkdirFactoryTest {
private String initialBranchForLastCloneCall;
private boolean workdirIsCached = false;
private File workdir;
@Before
public void initFactory() throws IOException {
WorkdirProvider workdirProvider = new WorkdirProvider(temporaryFolder.newFolder());
simpleWorkdirFactory = new SimpleWorkdirFactory<Closeable, Closeable, Context>(workdirProvider) {
CacheSupportingWorkdirProvider configurableTestWorkdirProvider = new CacheSupportingWorkdirProvider() {
@Override
public <R, W, C> SimpleWorkdirFactory.ParentAndClone<R, W> getWorkdir(Repository scmRepository, String requestedBranch, C context, SimpleWorkdirFactory.WorkdirInitializer<R, W> initializer, SimpleWorkdirFactory.WorkdirReclaimer<R, W> reclaimer) throws IOException {
workdir = workdirProvider.createNewWorkdir();
return initializer.initialize(workdir);
}
@Override
public boolean cache(Repository repository, File target) {
return workdirIsCached;
}
};
simpleWorkdirFactory = new SimpleWorkdirFactory<Closeable, Closeable, Context>(configurableTestWorkdirProvider) {
@Override
protected Repository getScmRepository(Context context) {
return REPOSITORY;
@@ -66,6 +81,11 @@ public class SimpleWorkdirFactoryTest {
repository.close();
}
@Override
protected ParentAndClone<Closeable, Closeable> reclaimRepository(Context context, File target, String initialBranch) throws IOException {
throw new UnsupportedOperationException();
}
@Override
protected void closeWorkdirInternal(Closeable workdir) throws Exception {
workdir.close();
@@ -74,7 +94,7 @@ public class SimpleWorkdirFactoryTest {
@Override
protected ParentAndClone<Closeable, Closeable> cloneRepository(Context context, File target, String initialBranch) {
initialBranchForLastCloneCall = initialBranch;
return new ParentAndClone<>(parent, clone);
return new ParentAndClone<>(parent, clone, target);
}
};
}
@@ -104,6 +124,23 @@ public class SimpleWorkdirFactoryTest {
verify(clone).close();
}
@Test
public void shouldDeleteWorkdirIfNotCached() {
Context context = new Context();
try (WorkingCopy<Closeable, Closeable> workingCopy = simpleWorkdirFactory.createWorkingCopy(context, null)) {}
assertThat(workdir).doesNotExist();
}
@Test
public void shouldNotDeleteWorkdirIfCached() {
Context context = new Context();
workdirIsCached = true;
try (WorkingCopy<Closeable, Closeable> workingCopy = simpleWorkdirFactory.createWorkingCopy(context, null)) {}
assertThat(workdir).exists();
}
@Test
public void shouldPropagateInitialBranch() {
Context context = new Context();

View File

@@ -30,10 +30,11 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.ScmTransportProtocol;
import sonia.scm.repository.GitUtil;
import sonia.scm.repository.GitWorkdirFactory;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.util.CacheSupportingWorkdirProvider;
import sonia.scm.repository.util.SimpleWorkdirFactory;
import sonia.scm.repository.util.WorkdirProvider;
import sonia.scm.util.SystemUtil;
import javax.inject.Inject;
@@ -46,7 +47,7 @@ import static sonia.scm.NotFoundException.notFound;
public class SimpleGitWorkdirFactory extends SimpleWorkdirFactory<Repository, Repository, GitContext> implements GitWorkdirFactory {
@Inject
public SimpleGitWorkdirFactory(WorkdirProvider workdirProvider) {
public SimpleGitWorkdirFactory(CacheSupportingWorkdirProvider workdirProvider) {
super(workdirProvider);
}
@@ -66,12 +67,17 @@ public class SimpleGitWorkdirFactory extends SimpleWorkdirFactory<Repository, Re
throw notFound(entity("Branch", initialBranch).in(context.getRepository()));
}
return new ParentAndClone<>(null, clone);
return new ParentAndClone<>(null, clone, target);
} catch (GitAPIException | IOException e) {
throw new InternalRepositoryException(context.getRepository(), "could not clone working copy of repository", e);
}
}
@Override
protected ParentAndClone<Repository, Repository> reclaimRepository(GitContext context, File target, String initialBranch) throws IOException {
return new ParentAndClone<>(null, GitUtil.open(target), target);
}
private String createScmTransportProtocolUri(File bareRepository) {
if (SystemUtil.isWindows()) {
return ScmTransportProtocol.NAME + ":///" + bareRepository.getAbsolutePath().replaceAll("\\\\", "/");

View File

@@ -44,6 +44,7 @@ import sonia.scm.repository.GitWorkdirFactory;
import sonia.scm.repository.Person;
import sonia.scm.repository.api.MergeCommandResult;
import sonia.scm.repository.api.MergeStrategy;
import sonia.scm.repository.util.NoneCachingWorkdirProvider;
import sonia.scm.repository.util.WorkdirProvider;
import sonia.scm.user.User;
@@ -424,7 +425,7 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase {
}
private GitMergeCommand createCommand(Consumer<Git> interceptor) {
return new GitMergeCommand(createContext(), new SimpleGitWorkdirFactory(new WorkdirProvider())) {
return new GitMergeCommand(createContext(), new SimpleGitWorkdirFactory(new NoneCachingWorkdirProvider(new WorkdirProvider()))) {
@Override
<R, W extends GitCloneWorker<R>> R inClone(Function<Git, W> workerSupplier, GitWorkdirFactory workdirFactory, String initialBranch) {
Function<Git, W> interceptedWorkerSupplier = git -> {

View File

@@ -27,6 +27,7 @@ package sonia.scm.repository.spi;
import org.junit.Rule;
import org.junit.Test;
import sonia.scm.repository.spi.MergeConflictResult.SingleMergeConflict;
import sonia.scm.repository.util.NoneCachingWorkdirProvider;
import sonia.scm.repository.util.WorkdirProvider;
import java.io.IOException;
@@ -91,7 +92,7 @@ public class GitMergeCommand_Conflict_Test extends AbstractGitCommandTestBase {
}
private MergeConflictResult computeMergeConflictResult(String branchToMerge, String targetBranch) {
GitMergeCommand gitMergeCommand = new GitMergeCommand(createContext(), new SimpleGitWorkdirFactory(new WorkdirProvider()));
GitMergeCommand gitMergeCommand = new GitMergeCommand(createContext(), new SimpleGitWorkdirFactory(new NoneCachingWorkdirProvider(new WorkdirProvider())));
MergeCommandRequest mergeCommandRequest = new MergeCommandRequest();
mergeCommandRequest.setBranchToMerge(branchToMerge);
mergeCommandRequest.setTargetBranch(targetBranch);

View File

@@ -42,6 +42,7 @@ import sonia.scm.BadRequestException;
import sonia.scm.ConcurrentModificationException;
import sonia.scm.NotFoundException;
import sonia.scm.repository.Person;
import sonia.scm.repository.util.NoneCachingWorkdirProvider;
import sonia.scm.repository.util.WorkdirProvider;
import sonia.scm.web.lfs.LfsBlobStoreFactory;
@@ -323,7 +324,7 @@ public class GitModifyCommandTest extends AbstractGitCommandTestBase {
}
private GitModifyCommand createCommand() {
return new GitModifyCommand(createContext(), new SimpleGitWorkdirFactory(new WorkdirProvider()), lfsBlobStoreFactory);
return new GitModifyCommand(createContext(), new SimpleGitWorkdirFactory(new NoneCachingWorkdirProvider(new WorkdirProvider())), lfsBlobStoreFactory);
}
@FunctionalInterface

View File

@@ -35,6 +35,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import sonia.scm.repository.Person;
import sonia.scm.repository.util.NoneCachingWorkdirProvider;
import sonia.scm.repository.util.WorkdirProvider;
import sonia.scm.store.Blob;
import sonia.scm.store.BlobStore;
@@ -130,7 +131,7 @@ public class GitModifyCommand_LFSTest extends AbstractGitCommandTestBase {
}
private GitModifyCommand createCommand() {
return new GitModifyCommand(createContext(), new SimpleGitWorkdirFactory(new WorkdirProvider()), lfsBlobStoreFactory);
return new GitModifyCommand(createContext(), new SimpleGitWorkdirFactory(new NoneCachingWorkdirProvider(new WorkdirProvider())), lfsBlobStoreFactory);
}
@Override

View File

@@ -38,6 +38,7 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import sonia.scm.repository.Person;
import sonia.scm.repository.util.NoneCachingWorkdirProvider;
import sonia.scm.repository.util.WorkdirProvider;
import sonia.scm.web.lfs.LfsBlobStoreFactory;
@@ -101,7 +102,7 @@ public class GitModifyCommand_withEmptyRepositoryTest extends AbstractGitCommand
}
private GitModifyCommand createCommand() {
return new GitModifyCommand(createContext(), new SimpleGitWorkdirFactory(new WorkdirProvider()), lfsBlobStoreFactory);
return new GitModifyCommand(createContext(), new SimpleGitWorkdirFactory(new NoneCachingWorkdirProvider(new WorkdirProvider())), lfsBlobStoreFactory);
}
@FunctionalInterface

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.repository.spi;
import org.eclipse.jgit.lib.Repository;
@@ -35,6 +35,7 @@ import sonia.scm.repository.GitRepositoryHandler;
import sonia.scm.repository.PreProcessorUtil;
import sonia.scm.repository.RepositoryManager;
import sonia.scm.repository.api.HookContextFactory;
import sonia.scm.repository.util.NoneCachingWorkdirProvider;
import sonia.scm.repository.util.WorkdirProvider;
import sonia.scm.repository.util.WorkingCopy;
@@ -66,7 +67,7 @@ public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase {
@Test
public void emptyPoolShouldCreateNewWorkdir() {
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(workdirProvider);
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(new NoneCachingWorkdirProvider(workdirProvider));
File masterRepo = createRepositoryDirectory();
try (WorkingCopy<Repository, Repository> workingCopy = factory.createWorkingCopy(createContext(), null)) {
@@ -84,7 +85,7 @@ public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase {
@Test
public void shouldCheckoutInitialBranch() {
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(workdirProvider);
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(new NoneCachingWorkdirProvider(workdirProvider));
try (WorkingCopy<Repository, Repository> workingCopy = factory.createWorkingCopy(createContext(), "test-branch")) {
assertThat(new File(workingCopy.getWorkingRepository().getWorkTree(), "a.txt"))
@@ -96,7 +97,7 @@ public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase {
@Test
public void shouldCheckoutDefaultBranch() {
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(workdirProvider);
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(new NoneCachingWorkdirProvider(workdirProvider));
try (WorkingCopy<Repository, Repository> workingCopy = factory.createWorkingCopy(createContext(), null)) {
assertThat(new File(workingCopy.getWorkingRepository().getWorkTree(), "a.txt"))
@@ -108,7 +109,7 @@ public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase {
@Test
public void cloneFromPoolShouldNotBeReused() {
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(workdirProvider);
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(new NoneCachingWorkdirProvider(workdirProvider));
File firstDirectory;
try (WorkingCopy<Repository, Repository> workingCopy = factory.createWorkingCopy(createContext(), null)) {
@@ -122,7 +123,7 @@ public class SimpleGitWorkdirFactoryTest extends AbstractGitCommandTestBase {
@Test
public void cloneFromPoolShouldBeDeletedOnClose() {
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(workdirProvider);
SimpleGitWorkdirFactory factory = new SimpleGitWorkdirFactory(new NoneCachingWorkdirProvider(workdirProvider));
File directory;
try (WorkingCopy<Repository, Repository> workingCopy = factory.createWorkingCopy(createContext(), null)) {

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.repository.spi;
import com.aragost.javahg.BaseRepository;
@@ -29,8 +29,8 @@ import com.aragost.javahg.Repository;
import com.aragost.javahg.commands.CloneCommand;
import com.aragost.javahg.commands.PullCommand;
import com.aragost.javahg.commands.flags.CloneCommandFlags;
import sonia.scm.repository.util.CacheSupportingWorkdirProvider;
import sonia.scm.repository.util.SimpleWorkdirFactory;
import sonia.scm.repository.util.WorkdirProvider;
import sonia.scm.web.HgRepositoryEnvironmentBuilder;
import javax.inject.Inject;
@@ -45,15 +45,13 @@ public class SimpleHgWorkdirFactory extends SimpleWorkdirFactory<Repository, Rep
private final Provider<HgRepositoryEnvironmentBuilder> hgRepositoryEnvironmentBuilder;
@Inject
public SimpleHgWorkdirFactory(Provider<HgRepositoryEnvironmentBuilder> hgRepositoryEnvironmentBuilder, WorkdirProvider workdirProvider) {
public SimpleHgWorkdirFactory(Provider<HgRepositoryEnvironmentBuilder> hgRepositoryEnvironmentBuilder, CacheSupportingWorkdirProvider workdirProvider) {
super(workdirProvider);
this.hgRepositoryEnvironmentBuilder = hgRepositoryEnvironmentBuilder;
}
@Override
public ParentAndClone<Repository, Repository> cloneRepository(HgCommandContext context, File target, String initialBranch) throws IOException {
BiConsumer<sonia.scm.repository.Repository, Map<String, String>> repositoryMapBiConsumer =
(repository, environment) -> hgRepositoryEnvironmentBuilder.get().buildFor(repository, null, environment);
Repository centralRepository = context.openWithSpecialEnvironment(repositoryMapBiConsumer);
Repository centralRepository = openCentral(context);
CloneCommand cloneCommand = CloneCommandFlags.on(centralRepository);
if (initialBranch != null) {
cloneCommand.updaterev(initialBranch);
@@ -62,7 +60,20 @@ public class SimpleHgWorkdirFactory extends SimpleWorkdirFactory<Repository, Rep
BaseRepository clone = Repository.open(target);
return new ParentAndClone<>(centralRepository, clone);
return new ParentAndClone<>(centralRepository, clone, target);
}
public Repository openCentral(HgCommandContext context) {
BiConsumer<sonia.scm.repository.Repository, Map<String, String>> repositoryMapBiConsumer =
(repository, environment) -> hgRepositoryEnvironmentBuilder.get().buildFor(repository, null, environment);
return context.openWithSpecialEnvironment(repositoryMapBiConsumer);
}
@Override
protected ParentAndClone<Repository, Repository> reclaimRepository(HgCommandContext context, File target, String initialBranch) throws IOException {
Repository centralRepository = openCentral(context);
BaseRepository clone = Repository.open(target);
return new ParentAndClone<>(centralRepository, clone, target);
}
@Override

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.repository.spi;
import com.aragost.javahg.commands.PullCommand;
@@ -32,6 +32,7 @@ import sonia.scm.repository.Branch;
import sonia.scm.repository.HgTestUtil;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.api.BranchRequest;
import sonia.scm.repository.util.NoneCachingWorkdirProvider;
import sonia.scm.repository.util.WorkdirProvider;
import sonia.scm.web.HgRepositoryEnvironmentBuilder;
@@ -49,7 +50,7 @@ public class HgBranchCommandTest extends AbstractHgCommandTestBase {
HgRepositoryEnvironmentBuilder hgRepositoryEnvironmentBuilder =
new HgRepositoryEnvironmentBuilder(handler, HgTestUtil.createHookManager());
workdirFactory = new SimpleHgWorkdirFactory(Providers.of(hgRepositoryEnvironmentBuilder), new WorkdirProvider()) {
workdirFactory = new SimpleHgWorkdirFactory(Providers.of(hgRepositoryEnvironmentBuilder), new NoneCachingWorkdirProvider(new WorkdirProvider())) {
@Override
public void configure(PullCommand pullCommand) {
// we do not want to configure http hooks in this unit test

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.repository.spi;
import com.google.inject.util.Providers;
@@ -35,6 +35,7 @@ import sonia.scm.NotFoundException;
import sonia.scm.repository.HgHookManager;
import sonia.scm.repository.HgTestUtil;
import sonia.scm.repository.Person;
import sonia.scm.repository.util.NoneCachingWorkdirProvider;
import sonia.scm.repository.util.WorkdirProvider;
import sonia.scm.web.HgRepositoryEnvironmentBuilder;
@@ -55,7 +56,7 @@ public class HgModifyCommandTest extends AbstractHgCommandTestBase {
public void initHgModifyCommand() {
HgHookManager hookManager = HgTestUtil.createHookManager();
HgRepositoryEnvironmentBuilder environmentBuilder = new HgRepositoryEnvironmentBuilder(handler, hookManager);
SimpleHgWorkdirFactory workdirFactory = new SimpleHgWorkdirFactory(Providers.of(environmentBuilder), new WorkdirProvider()) {
SimpleHgWorkdirFactory workdirFactory = new SimpleHgWorkdirFactory(Providers.of(environmentBuilder), new NoneCachingWorkdirProvider(new WorkdirProvider())) {
@Override
public void configure(com.aragost.javahg.commands.PullCommand pullCommand) {
// we do not want to configure http hooks in this unit test

View File

@@ -32,16 +32,17 @@ import org.tmatesoft.svn.core.wc2.SvnTarget;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.Repository;
import sonia.scm.repository.SvnWorkDirFactory;
import sonia.scm.repository.util.CacheSupportingWorkdirProvider;
import sonia.scm.repository.util.SimpleWorkdirFactory;
import sonia.scm.repository.util.WorkdirProvider;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
public class SimpleSvnWorkDirFactory extends SimpleWorkdirFactory<File, File, SvnContext> implements SvnWorkDirFactory {
@Inject
public SimpleSvnWorkDirFactory(WorkdirProvider workdirProvider) {
public SimpleSvnWorkDirFactory(CacheSupportingWorkdirProvider workdirProvider) {
super(workdirProvider);
}
@@ -73,7 +74,12 @@ public class SimpleSvnWorkDirFactory extends SimpleWorkdirFactory<File, File, Sv
svnOperationFactory.dispose();
}
return new ParentAndClone<>(context.getDirectory(), workingCopy);
return new ParentAndClone<>(context.getDirectory(), workingCopy, workingCopy);
}
@Override
protected ParentAndClone<File, File> reclaimRepository(SvnContext context, File target, String initialBranch) throws IOException {
return new ParentAndClone<>(context.getDirectory(), target, target);
}
@Override

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.repository.spi;
import org.junit.Before;
@@ -30,6 +30,7 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.tmatesoft.svn.core.SVNException;
import sonia.scm.repository.Repository;
import sonia.scm.repository.util.NoneCachingWorkdirProvider;
import sonia.scm.repository.util.WorkdirProvider;
import sonia.scm.repository.util.WorkingCopy;
@@ -53,7 +54,7 @@ public class SimpleSvnWorkDirFactoryTest extends AbstractSvnCommandTestBase {
@Test
public void shouldCheckoutLatestRevision() throws SVNException, IOException {
SimpleSvnWorkDirFactory factory = new SimpleSvnWorkDirFactory(workdirProvider);
SimpleSvnWorkDirFactory factory = new SimpleSvnWorkDirFactory(new NoneCachingWorkdirProvider(workdirProvider));
try (WorkingCopy<File, File> workingCopy = factory.createWorkingCopy(createContext(), null)) {
assertThat(new File(workingCopy.getWorkingRepository(), "a.txt"))
@@ -65,7 +66,7 @@ public class SimpleSvnWorkDirFactoryTest extends AbstractSvnCommandTestBase {
@Test
public void cloneFromPoolshouldNotBeReused() {
SimpleSvnWorkDirFactory factory = new SimpleSvnWorkDirFactory(workdirProvider);
SimpleSvnWorkDirFactory factory = new SimpleSvnWorkDirFactory(new NoneCachingWorkdirProvider(workdirProvider));
File firstDirectory;
try (WorkingCopy<File, File> workingCopy = factory.createWorkingCopy(createContext(), null)) {
@@ -79,7 +80,7 @@ public class SimpleSvnWorkDirFactoryTest extends AbstractSvnCommandTestBase {
@Test
public void shouldDeleteCloneOnClose() {
SimpleSvnWorkDirFactory factory = new SimpleSvnWorkDirFactory(workdirProvider);
SimpleSvnWorkDirFactory factory = new SimpleSvnWorkDirFactory(new NoneCachingWorkdirProvider(workdirProvider));
File directory;
File workingRepository;
@@ -94,7 +95,7 @@ public class SimpleSvnWorkDirFactoryTest extends AbstractSvnCommandTestBase {
@Test
public void shouldReturnRepository() {
SimpleSvnWorkDirFactory factory = new SimpleSvnWorkDirFactory(workdirProvider);
SimpleSvnWorkDirFactory factory = new SimpleSvnWorkDirFactory(new NoneCachingWorkdirProvider(workdirProvider));
Repository scmRepository = factory.getScmRepository(createContext());
assertThat(scmRepository).isSameAs(repository);
}

View File

@@ -33,6 +33,7 @@ import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import sonia.scm.AlreadyExistsException;
import sonia.scm.repository.Person;
import sonia.scm.repository.util.NoneCachingWorkdirProvider;
import sonia.scm.repository.util.WorkdirProvider;
import sonia.scm.repository.util.WorkingCopy;
@@ -56,7 +57,7 @@ public class SvnModifyCommandTest extends AbstractSvnCommandTestBase {
@Before
public void initSvnModifyCommand() {
context = createContext();
workDirFactory = new SimpleSvnWorkDirFactory(new WorkdirProvider(context.getDirectory()));
workDirFactory = new SimpleSvnWorkDirFactory(new NoneCachingWorkdirProvider(new WorkdirProvider(context.getDirectory())));
svnModifyCommand = new SvnModifyCommand(context, workDirFactory);
}

View File

@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package sonia.scm.lifecycle.modules;
import com.google.common.base.Throwables;
@@ -77,6 +77,7 @@ public class ApplicationModuleProvider implements ModuleProvider {
}
moduleList.add(new MapperModule());
moduleList.add(new ExecutorModule());
moduleList.add(new WorkdirModule(pluginLoader));
return moduleList;
}

View File

@@ -0,0 +1,50 @@
/*
* 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.lifecycle.modules;
import com.google.inject.AbstractModule;
import sonia.scm.plugin.PluginLoader;
import sonia.scm.repository.util.CacheSupportingWorkdirProvider;
public class WorkdirModule extends AbstractModule {
public static final String DEFAULT_WORKDIR_CACHE_STRATEGY = "sonia.scm.repository.util.NoneCachingWorkdirProvider";
public static final String WORKDIR_CACHE_STRATEGY_PROPERTY = "scm.workdirCacheStrategy";
private final PluginLoader pluginLoader;
public WorkdirModule(PluginLoader pluginLoader) {
this.pluginLoader = pluginLoader;
}
@Override
protected void configure() {
try {
String workdirCacheStrategy = System.getProperty(WORKDIR_CACHE_STRATEGY_PROPERTY, DEFAULT_WORKDIR_CACHE_STRATEGY);
Class<? extends CacheSupportingWorkdirProvider> strategyClass = (Class<? extends CacheSupportingWorkdirProvider>) pluginLoader.getUberClassLoader().loadClass(workdirCacheStrategy);
bind(CacheSupportingWorkdirProvider.class).to(strategyClass);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}