# HG changeset patch # User Dmitry Neverov # Date 1327663669 -14400 # Node ID 69d66e4f60e31b947a2d6439c3c5054ee9907f85 # Parent 5b3280c0fb2f9b9484c00726ab34265fc722b88f Extract MirrorManager interface diff -r 5b3280c0fb2f -r 69d66e4f60e3 mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialAgentSideVcsSupport.java --- a/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialAgentSideVcsSupport.java Mon Jan 23 13:21:27 2012 +0400 +++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialAgentSideVcsSupport.java Fri Jan 27 15:27:49 2012 +0400 @@ -37,7 +37,7 @@ @NotNull final HgPathProvider hgPathProvider) { myConfig = pluginConfig; myHgPathProvider = hgPathProvider; - myMirrorManager = new MirrorManager(myConfig.getCachesDir()); + myMirrorManager = new MirrorManagerImpl(myConfig); } public IncludeRuleUpdater getUpdater(@NotNull final VcsRoot vcsRoot, @NotNull final CheckoutRules checkoutRules, @NotNull final String toVersion, @NotNull final File checkoutDirectory, @NotNull final AgentRunningBuild build, boolean cleanCheckoutRequested) throws VcsException { diff -r 5b3280c0fb2f -r 69d66e4f60e3 mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManager.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManager.java Mon Jan 23 13:21:27 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManager.java Fri Jan 27 15:27:49 2012 +0400 @@ -1,46 +1,14 @@ package jetbrains.buildServer.buildTriggers.vcs.mercurial; -import com.intellij.openapi.diagnostic.Logger; -import jetbrains.buildServer.util.FileUtil; -import jetbrains.buildServer.util.Hash; import org.jetbrains.annotations.NotNull; import java.io.File; -import java.io.IOException; -import java.util.*; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - -import static jetbrains.buildServer.util.FileUtil.isEmptyDir; +import java.util.List; /** - * Manages local mirrors of remote repositories. - * Each unique url get unique local mirror. Each mirror is used for one url only. * @author dmitry.neverov */ -public final class MirrorManager { - - private static Logger LOG = Logger.getInstance(MirrorManager.class.getName()); - private static final String MIRROR_DIR_PREFIX = "hg_"; - private static final String MAPPING_FILE_NAME = "map"; - - private final ReadWriteLock myLock = new ReentrantReadWriteLock(); - private final File myRootDir; - /*Only one thread read or write to this file, it is protected by myLock.writeLock()*/ - private final File myMappingFile; - /*Protected by myLock*/ - private final Map myMirrors = new HashMap(); - private HashCalculator myHash = new StandartHash(); - - /** - * @param rootDir root directory where all mirrors are stored - */ - public MirrorManager(File rootDir) { - myRootDir = rootDir; - myMappingFile = new File(myRootDir, MAPPING_FILE_NAME); - readMappingFromFile(); - } - +public interface MirrorManager { /** * Get directory of local mirror repository for specified url, if directory is not exists it is created @@ -48,29 +16,14 @@ * @return see above */ @NotNull - public File getMirrorDir(@NotNull final String url) { - File result = getMirrorDirWithLock(url); - if (result == null) { - result = createDirFor(url); - } - return result; - } - + public File getMirrorDir(@NotNull final String url); /** * Get all local mirror repository dirs * @return see above */ @NotNull - public List getMirrors() { - myLock.readLock().lock(); - try { - return new ArrayList(myMirrors.values()); - } finally { - myLock.readLock().unlock(); - } - } - + public List getMirrors(); /** * Forget specified dir. After call to this method with non-empty dir, @@ -80,201 +33,6 @@ * * @param dir dir of interest */ - public void forgetDir(@NotNull final File dir) { - myLock.writeLock().lock(); - try { - removeMappingsToDir(dir); - saveMappingToFile(); - } finally { - myLock.writeLock().unlock(); - } - } - - private void removeMappingsToDir(@NotNull final File dir) { - Set keysToRemove = getUrlsMappedToDir(dir); - for (String key : keysToRemove) { - myMirrors.remove(key); - } - } - - private Set getUrlsMappedToDir(@NotNull final File dir) { - Set urlsMappedToDir = new HashSet(); - for (Map.Entry entry : myMirrors.entrySet()) { - File f = entry.getValue(); - if (f.equals(dir)) - urlsMappedToDir.add(entry.getKey()); - } - return urlsMappedToDir; - } - - - //for tests only - void setHashCalculator(HashCalculator hash) { - myHash = hash; - } - - - private File createDirFor(String url) { - File result; - myLock.writeLock().lock(); - try { - File mirrorDir = getUniqueDir(url); - result = saveMappingIfAbsent(url, mirrorDir); - } finally { - myLock.writeLock().unlock(); - } - if (!result.exists()) { - result.mkdirs(); - } - return result; - } - - - private File getMirrorDirWithLock(String url) { - myLock.readLock().lock(); - try { - return myMirrors.get(url); - } finally { - myLock.readLock().unlock(); - } - } - - - //should be called with myLock.writeLock() held - private File saveMappingIfAbsent(String url, File mirrorDir) { - File existing = myMirrors.get(url); - if (existing != null) { - return existing; - } else { - myMirrors.put(url, mirrorDir); - saveMappingToFile(); - return mirrorDir; - } - } - - - private File getUniqueDir(String url) { - myLock.readLock().lock(); - try { - String dirName = MIRROR_DIR_PREFIX + hash(normalize(url)); - File result = PathUtil.getCanonicalFile(new File(myRootDir, dirName)); - while (isUsedForOtherUrl(result, url) || !isEmptyDir(result)) { - dirName = MIRROR_DIR_PREFIX + hash(result.getName()); - result = PathUtil.getCanonicalFile(new File(myRootDir, dirName)); - } - return result; - } finally { - myLock.readLock().unlock(); - } - } - + public void forgetDir(@NotNull final File dir); - private boolean isUsedForOtherUrl(File repositoryDir, String url) { - myLock.readLock().lock(); - try { - for (Map.Entry mirror : myMirrors.entrySet()) { - String mirrorUrl = mirror.getKey(); - File mirrorDir = mirror.getValue(); - if (mirrorDir.equals(repositoryDir) && !mirrorUrl.equals(url)) { - return true; - } - } - return false; - } finally { - myLock.readLock().unlock(); - } - } - - - private String hash(String value) { - return String.valueOf(myHash.calc(value)); - } - - - private static String normalize(final String path) { - String normalized = PathUtil.normalizeSeparator(path); - if (path.endsWith("/")) { - return normalized.substring(0, normalized.length()-1); - } - return normalized; - } - - - private void readMappingFromFile() { - myLock.writeLock().lock(); - try { - LOG.debug("Parse mapping file " + myMappingFile.getAbsolutePath()); - for (String line : readLines()) { - int separatorIndex = line.lastIndexOf(" = "); - if (separatorIndex == -1) { - if (!line.equals("")) - LOG.warn("Cannot parse mapping '" + line + "', skip it."); - } else { - String url = line.substring(0, separatorIndex); - String dirName = line.substring(separatorIndex + 3); - File repositoryDir = PathUtil.getCanonicalFile(new File(myRootDir, dirName)); - if (isUsedForOtherUrl(repositoryDir, url)) { - LOG.error("Skip mapping " + line + ": " + dirName + " is used for url other than " + url); - } else { - myMirrors.put(url, PathUtil.getCanonicalFile(new File(myRootDir, dirName))); - } - } - } - } finally { - myLock.writeLock().unlock(); - } - } - - /*Should be called with myLock.writeLock() held*/ - private List readLines() { - if (myMappingFile.exists()) { - try { - return FileUtil.readFile(myMappingFile); - } catch (IOException e) { - LOG.error("Error while reading a mapping file at " + myMappingFile.getAbsolutePath() + " starting with empty mapping", e); - return new ArrayList(); - } - } else { - LOG.debug("No mapping file found at " + myMappingFile.getAbsolutePath() + " starting with empty mapping"); - File parentDir = myMappingFile.getParentFile(); - if (!parentDir.exists() && !parentDir.mkdirs()) { - LOG.error("Cannot create local mirrors dir at " + parentDir.getAbsolutePath()); - } else { - try { - if (!myMappingFile.createNewFile()) - LOG.warn("Someone else creates a mapping file " + myMappingFile.getAbsolutePath() + ", will use it"); - } catch (IOException e) { - LOG.error("Cannot create a mapping file at " + myMappingFile.getAbsolutePath(), e); - } - } - return new ArrayList(); - } - } - - - private void saveMappingToFile() { - myLock.writeLock().lock(); - try { - StringBuilder sb = new StringBuilder(); - for (Map.Entry mirror : myMirrors.entrySet()) { - String url = mirror.getKey(); - String dir = mirror.getValue().getName(); - sb.append(url).append(" = ").append(dir).append("\n"); - } - FileUtil.writeFile(myMappingFile, sb.toString()); - } finally { - myLock.writeLock().unlock(); - } - } - - - final static class StandartHash implements HashCalculator { - public long calc(String value) { - return Hash.calc(value); - } - } - - public static interface HashCalculator { - long calc(String value); - } } diff -r 5b3280c0fb2f -r 69d66e4f60e3 mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManagerImpl.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManagerImpl.java Fri Jan 27 15:27:49 2012 +0400 @@ -0,0 +1,277 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +import com.intellij.openapi.diagnostic.Logger; +import jetbrains.buildServer.util.FileUtil; +import jetbrains.buildServer.util.Hash; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import static jetbrains.buildServer.util.FileUtil.isEmptyDir; + +/** + * Manages local mirrors of remote repositories. + * Each unique url get unique local mirror. Each mirror is used for one url only. + * @author dmitry.neverov + */ +public final class MirrorManagerImpl implements MirrorManager { + + private static Logger LOG = Logger.getInstance(MirrorManagerImpl.class.getName()); + private static final String MIRROR_DIR_PREFIX = "hg_"; + private static final String MAPPING_FILE_NAME = "map"; + + private final ReadWriteLock myLock = new ReentrantReadWriteLock(); + private final File myRootDir; + /*Only one thread read or write to this file, it is protected by myLock.writeLock()*/ + private final File myMappingFile; + /*Protected by myLock*/ + private final Map myMirrors = new HashMap(); + private HashCalculator myHash = new StandartHash(); + + public MirrorManagerImpl(@NotNull PluginConfig config) { + myRootDir = config.getCachesDir(); + myMappingFile = new File(myRootDir, MAPPING_FILE_NAME); + readMappingFromFile(); + } + + + /** + * Get directory of local mirror repository for specified url, if directory is not exists it is created + * @param url url of interest + * @return see above + */ + @NotNull + public File getMirrorDir(@NotNull final String url) { + File result = getMirrorDirWithLock(url); + if (result == null) { + result = createDirFor(url); + } + return result; + } + + + /** + * Get all local mirror repository dirs + * @return see above + */ + @NotNull + public List getMirrors() { + myLock.readLock().lock(); + try { + return new ArrayList(myMirrors.values()); + } finally { + myLock.readLock().unlock(); + } + } + + + /** + * Forget specified dir. After call to this method with non-empty dir, + * all urls which were mapped to this dir will be mapped to another. + * If dir is empty, subsequent call getMirrorDir(dir) will return the + * same dir. + * + * @param dir dir of interest + */ + public void forgetDir(@NotNull final File dir) { + myLock.writeLock().lock(); + try { + removeMappingsToDir(dir); + saveMappingToFile(); + } finally { + myLock.writeLock().unlock(); + } + } + + private void removeMappingsToDir(@NotNull final File dir) { + Set keysToRemove = getUrlsMappedToDir(dir); + for (String key : keysToRemove) { + myMirrors.remove(key); + } + } + + private Set getUrlsMappedToDir(@NotNull final File dir) { + Set urlsMappedToDir = new HashSet(); + for (Map.Entry entry : myMirrors.entrySet()) { + File f = entry.getValue(); + if (f.equals(dir)) + urlsMappedToDir.add(entry.getKey()); + } + return urlsMappedToDir; + } + + + //for tests only + void setHashCalculator(HashCalculator hash) { + myHash = hash; + } + + + private File createDirFor(String url) { + File result; + myLock.writeLock().lock(); + try { + File mirrorDir = getUniqueDir(url); + result = saveMappingIfAbsent(url, mirrorDir); + } finally { + myLock.writeLock().unlock(); + } + if (!result.exists()) { + result.mkdirs(); + } + return result; + } + + + private File getMirrorDirWithLock(String url) { + myLock.readLock().lock(); + try { + return myMirrors.get(url); + } finally { + myLock.readLock().unlock(); + } + } + + + //should be called with myLock.writeLock() held + private File saveMappingIfAbsent(String url, File mirrorDir) { + File existing = myMirrors.get(url); + if (existing != null) { + return existing; + } else { + myMirrors.put(url, mirrorDir); + saveMappingToFile(); + return mirrorDir; + } + } + + + private File getUniqueDir(String url) { + myLock.readLock().lock(); + try { + String dirName = MIRROR_DIR_PREFIX + hash(normalize(url)); + File result = PathUtil.getCanonicalFile(new File(myRootDir, dirName)); + while (isUsedForOtherUrl(result, url) || !isEmptyDir(result)) { + dirName = MIRROR_DIR_PREFIX + hash(result.getName()); + result = PathUtil.getCanonicalFile(new File(myRootDir, dirName)); + } + return result; + } finally { + myLock.readLock().unlock(); + } + } + + + private boolean isUsedForOtherUrl(File repositoryDir, String url) { + myLock.readLock().lock(); + try { + for (Map.Entry mirror : myMirrors.entrySet()) { + String mirrorUrl = mirror.getKey(); + File mirrorDir = mirror.getValue(); + if (mirrorDir.equals(repositoryDir) && !mirrorUrl.equals(url)) { + return true; + } + } + return false; + } finally { + myLock.readLock().unlock(); + } + } + + + private String hash(String value) { + return String.valueOf(myHash.calc(value)); + } + + + private static String normalize(final String path) { + String normalized = PathUtil.normalizeSeparator(path); + if (path.endsWith("/")) { + return normalized.substring(0, normalized.length()-1); + } + return normalized; + } + + + private void readMappingFromFile() { + myLock.writeLock().lock(); + try { + LOG.debug("Parse mapping file " + myMappingFile.getAbsolutePath()); + for (String line : readLines()) { + int separatorIndex = line.lastIndexOf(" = "); + if (separatorIndex == -1) { + if (!line.equals("")) + LOG.warn("Cannot parse mapping '" + line + "', skip it."); + } else { + String url = line.substring(0, separatorIndex); + String dirName = line.substring(separatorIndex + 3); + File repositoryDir = PathUtil.getCanonicalFile(new File(myRootDir, dirName)); + if (isUsedForOtherUrl(repositoryDir, url)) { + LOG.error("Skip mapping " + line + ": " + dirName + " is used for url other than " + url); + } else { + myMirrors.put(url, PathUtil.getCanonicalFile(new File(myRootDir, dirName))); + } + } + } + } finally { + myLock.writeLock().unlock(); + } + } + + /*Should be called with myLock.writeLock() held*/ + private List readLines() { + if (myMappingFile.exists()) { + try { + return FileUtil.readFile(myMappingFile); + } catch (IOException e) { + LOG.error("Error while reading a mapping file at " + myMappingFile.getAbsolutePath() + " starting with empty mapping", e); + return new ArrayList(); + } + } else { + LOG.debug("No mapping file found at " + myMappingFile.getAbsolutePath() + " starting with empty mapping"); + File parentDir = myMappingFile.getParentFile(); + if (!parentDir.exists() && !parentDir.mkdirs()) { + LOG.error("Cannot create local mirrors dir at " + parentDir.getAbsolutePath()); + } else { + try { + if (!myMappingFile.createNewFile()) + LOG.warn("Someone else creates a mapping file " + myMappingFile.getAbsolutePath() + ", will use it"); + } catch (IOException e) { + LOG.error("Cannot create a mapping file at " + myMappingFile.getAbsolutePath(), e); + } + } + return new ArrayList(); + } + } + + + private void saveMappingToFile() { + myLock.writeLock().lock(); + try { + StringBuilder sb = new StringBuilder(); + for (Map.Entry mirror : myMirrors.entrySet()) { + String url = mirror.getKey(); + String dir = mirror.getValue().getName(); + sb.append(url).append(" = ").append(dir).append("\n"); + } + FileUtil.writeFile(myMappingFile, sb.toString()); + } finally { + myLock.writeLock().unlock(); + } + } + + + final static class StandartHash implements HashCalculator { + public long calc(String value) { + return Hash.calc(value); + } + } + + public static interface HashCalculator { + long calc(String value); + } +} diff -r 5b3280c0fb2f -r 69d66e4f60e3 mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java --- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Mon Jan 23 13:21:27 2012 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Fri Jan 27 15:27:49 2012 +0400 @@ -60,7 +60,7 @@ private ConcurrentMap myWorkDirLocks= new ConcurrentHashMap(); private VcsManager myVcsManager; private File myDefaultWorkFolderParent; - private MirrorManager myMirrorManager; + private final MirrorManager myMirrorManager; private final ServerPluginConfig myConfig; private final HgPathProvider myHgPathProvider; private final CommandFactory myCommandFactory; @@ -76,7 +76,7 @@ myVcsManager = vcsManager; myConfig = config; myDefaultWorkFolderParent = myConfig.getCachesDir(); - myMirrorManager = new MirrorManager(myDefaultWorkFolderParent); + myMirrorManager = new MirrorManagerImpl(config); myHgPathProvider = hgPathProvider; myCommandFactory = commandFactory; dispatcher.addListener(new BuildServerAdapter() { diff -r 5b3280c0fb2f -r 69d66e4f60e3 mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManagerTest.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManagerTest.java Mon Jan 23 13:21:27 2012 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManagerTest.java Fri Jan 27 15:27:49 2012 +0400 @@ -3,6 +3,7 @@ import jetbrains.buildServer.TempFiles; import jetbrains.buildServer.util.Hash; import junit.framework.TestCase; +import org.jetbrains.annotations.NotNull; import org.testng.annotations.AfterMethod; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; @@ -19,14 +20,19 @@ private TempFiles myTempFiles; private File myRootDir; - private MirrorManager myManager; + private MirrorManagerImpl myManager; @BeforeMethod public void setUp() throws Exception { myTempFiles = new TempFiles(); myRootDir = myTempFiles.createTempDir(); - myManager = new MirrorManager(myRootDir); + myManager = new MirrorManagerImpl(new PluginConfig() { + @NotNull + public File getCachesDir() { + return myRootDir; + } + }); } @AfterMethod @@ -67,7 +73,7 @@ final String url1 = "hg://some.com/repository.hg"; final String url2 = "hg://other.com/repository.hg"; - MirrorManager.HashCalculator hashWithCollision = new MirrorManager.HashCalculator() { + MirrorManagerImpl.HashCalculator hashWithCollision = new MirrorManagerImpl.HashCalculator() { public long calc(String value) { if (value.equals(url1) || value.equals(url2)) { return 0;//emulate collision @@ -78,11 +84,23 @@ }; //alone they get dir with the same name: - MirrorManager mm1 = new MirrorManager(myTempFiles.createTempDir()); + final File dir1 = myTempFiles.createTempDir(); + MirrorManagerImpl mm1 = new MirrorManagerImpl(new PluginConfig() { + @NotNull + public File getCachesDir() { + return dir1; + } + }); mm1.setHashCalculator(hashWithCollision); File separateMirrorDir1 = mm1.getMirrorDir(url1); - MirrorManager mm2 = new MirrorManager(myTempFiles.createTempDir()); + final File dir2 = myTempFiles.createTempDir(); + MirrorManagerImpl mm2 = new MirrorManagerImpl(new PluginConfig() { + @NotNull + public File getCachesDir() { + return dir2; + } + }); mm2.setHashCalculator(hashWithCollision); File separateMirrorDir2 = mm2.getMirrorDir(url2); @@ -102,7 +120,12 @@ File mirrorDir2 = myManager.getMirrorDir(url2); //emulate restart by creating a new manager for the same rootDir - MirrorManager manager = new MirrorManager(myRootDir); + MirrorManagerImpl manager = new MirrorManagerImpl(new PluginConfig() { + @NotNull + public File getCachesDir() { + return myRootDir; + } + }); assertEquals(2, manager.getMirrors().size()); assertTrue(manager.getMirrors().contains(mirrorDir1)); diff -r 5b3280c0fb2f -r 69d66e4f60e3 mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SettingsTest.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SettingsTest.java Mon Jan 23 13:21:27 2012 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SettingsTest.java Fri Jan 27 15:27:49 2012 +0400 @@ -15,14 +15,11 @@ */ package jetbrains.buildServer.buildTriggers.vcs.mercurial; -import jetbrains.buildServer.TempFiles; import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings; import jetbrains.buildServer.vcs.VcsRoot; import jetbrains.buildServer.vcs.impl.VcsRootImpl; import junit.framework.TestCase; import org.jetbrains.annotations.NotNull; -import org.testng.annotations.AfterMethod; -import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; /** @@ -31,21 +28,6 @@ @Test public class SettingsTest extends TestCase { - private TempFiles myTempFiles = new TempFiles(); - private MirrorManager myMirrorManager; - - @Override - @BeforeMethod - public void setUp() throws Exception { - myMirrorManager = new MirrorManager(myTempFiles.createTempDir()); - } - - @Override - @AfterMethod - public void tearDown() throws Exception { - myTempFiles.cleanup(); - } - public void test_url_without_credentials() { VcsRootImpl vcsRoot = createVcsRoot("http://host.com/path"); Settings settings = createSettings(vcsRoot); diff -r 5b3280c0fb2f -r 69d66e4f60e3 mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTestCase.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTestCase.java Mon Jan 23 13:21:27 2012 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTestCase.java Fri Jan 27 15:27:49 2012 +0400 @@ -69,11 +69,9 @@ } TempFiles tf = new TempFiles(); - File parentDir = tf.createTempDir(); - - MirrorManager mirrorManager = new MirrorManager(parentDir); VcsRoot vcsRoot = new VcsRootImpl(1, vcsRootProps); - ServerPluginConfig config = new ServerPluginConfigBuilder().build(); + ServerPluginConfig config = new ServerPluginConfigBuilder().cachesDir(tf.createTempDir()).build(); + MirrorManager mirrorManager = new MirrorManagerImpl(config); Settings settings = new Settings(new ServerHgPathProvider(config), vcsRoot); final File workingDir = mirrorManager.getMirrorDir(settings.getRepositoryUrlWithCredentials()); settings.setCustomWorkingDir(workingDir);