Mercurial > hg > mercurial
changeset 21:c76d6a2b27f6
proper synchronization for working directories
cleanup unused working folders
author | Pavel.Sher |
---|---|
date | Thu, 17 Jul 2008 00:44:10 +0400 |
parents | 90f5e574fb73 |
children | 0d6f27953b30 |
files | mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java mercurial.ipr mercurial/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java mercurial/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/PathUtil.java mercurial/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Settings.java |
diffstat | 5 files changed, 117 insertions(+), 16 deletions(-) [+] |
line wrap: on
line diff
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java Wed Jul 16 19:22:52 2008 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java Thu Jul 17 00:44:10 2008 +0400 @@ -1,7 +1,10 @@ package jetbrains.buildServer.buildTriggers.vcs.mercurial; +import jetbrains.buildServer.MockSupport; import jetbrains.buildServer.TempFiles; +import jetbrains.buildServer.serverSide.SBuildServer; import jetbrains.buildServer.serverSide.ServerPaths; +import jetbrains.buildServer.util.SimpleExecutor; import jetbrains.buildServer.vcs.*; import jetbrains.buildServer.vcs.impl.VcsRootImpl; import jetbrains.buildServer.vcs.patches.PatchBuilderImpl; @@ -15,27 +18,37 @@ import java.io.File; import java.io.IOException; import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; @Test public class MercurialVcsSupportTest extends PatchTestCase { private MercurialVcsSupport myVcs; private TempFiles myTempFiles; + private MockSupport myMockSupport; @BeforeMethod protected void setUp() throws Exception { super.setUp(); + myMockSupport = new MockSupport(); + myMockSupport.setUpMocks(); + Mock vcsManagerMock = new Mock(VcsManager.class); vcsManagerMock.stubs().method("registerVcsSupport"); + Mock serverMock = new Mock(SBuildServer.class); + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + serverMock.stubs().method("getExecutor").will(myMockSupport.returnValue(executor)); myTempFiles = new TempFiles(); File systemDir = myTempFiles.createTempDir(); ServerPaths sp = new ServerPaths(systemDir.getAbsolutePath(), systemDir.getAbsolutePath()); assertTrue(new File(sp.getCachesDir()).mkdirs()); - myVcs = new MercurialVcsSupport((VcsManager)vcsManagerMock.proxy(), sp); + myVcs = new MercurialVcsSupport((VcsManager)vcsManagerMock.proxy(), sp, (SBuildServer)serverMock.proxy()); } @AfterMethod - protected void tearDown() { + protected void tearDown() throws Exception { + myMockSupport.tearDownMocks(); myTempFiles.cleanup(); }
--- a/mercurial.ipr Wed Jul 16 19:22:52 2008 +0400 +++ b/mercurial.ipr Thu Jul 17 00:44:10 2008 +0400 @@ -96,10 +96,6 @@ <SplitterProportionsDataImpl /> </option> </component> - <component name="ErrorTreeViewConfiguration"> - <option name="IS_AUTOSCROLL_TO_SOURCE" value="false" /> - <option name="HIDE_WARNINGS" value="false" /> - </component> <component name="ExportToHTMLSettings"> <option name="PRINT_LINE_NUMBERS" value="false" /> <option name="OPEN_IN_BROWSER" value="false" />
--- a/mercurial/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Wed Jul 16 19:22:52 2008 +0400 +++ b/mercurial/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Thu Jul 17 00:44:10 2008 +0400 @@ -7,6 +7,7 @@ import jetbrains.buildServer.log.Loggers; import jetbrains.buildServer.serverSide.InvalidProperty; import jetbrains.buildServer.serverSide.PropertiesProcessor; +import jetbrains.buildServer.serverSide.SBuildServer; import jetbrains.buildServer.serverSide.ServerPaths; import jetbrains.buildServer.util.FileUtil; import jetbrains.buildServer.vcs.*; @@ -19,6 +20,11 @@ import java.io.FileInputStream; import java.io.IOException; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; /** * Mercurial VCS plugin for TeamCity works as follows: @@ -34,10 +40,50 @@ */ public class MercurialVcsSupport extends VcsSupport implements CollectChangesByIncludeRule { private ServerPaths myServerPaths; + private ConcurrentMap<String, Lock> myWorkDirLocks= new ConcurrentHashMap<String, Lock>(); + private static final int OLD_WORK_DIRS_CLEANUP_PERIOD = 600; + private VcsManager myVcsManager; - public MercurialVcsSupport(@NotNull VcsManager vcsManager, @NotNull ServerPaths paths) { + public MercurialVcsSupport(@NotNull final VcsManager vcsManager, + @NotNull ServerPaths paths, + @NotNull SBuildServer server) { vcsManager.registerVcsSupport(this); myServerPaths = paths; + myVcsManager = vcsManager; + server.getExecutor().scheduleAtFixedRate(new Runnable() { + public void run() { + removeOldWorkFolders(); + } + }, 0, OLD_WORK_DIRS_CLEANUP_PERIOD, TimeUnit.SECONDS); + + } + + private void removeOldWorkFolders() { + File workFoldersParent = new File(myServerPaths.getCachesDir(), "mercurial"); + if (!workFoldersParent.isDirectory()) return; + + Set<File> dirNames = new HashSet<File>(); + File[] files = workFoldersParent.listFiles(new FileFilter() { + public boolean accept(final File file) { + return file.isDirectory() && file.getName().startsWith(Settings.DEFAULT_WORK_DIR_PREFIX); + } + }); + if (files != null) { + for (File f: files) { + dirNames.add(FileUtil.getCanonicalFile(f)); + } + } + + for (VcsRoot vcsRoot: myVcsManager.getAllRegisteredVcsRoots()) { + if (getName().equals(vcsRoot.getVcsName())) { + Settings s = new Settings(myServerPaths, vcsRoot); + dirNames.remove(FileUtil.getCanonicalFile(new File(s.getWorkingDir()))); + } + } + + for (File f: dirNames) { + FileUtil.delete(f); + } } public List<ModificationData> collectBuildChanges(final VcsRoot root, @@ -87,7 +133,7 @@ private List<VcsChange> toVcsChanges(final List<ModifiedFile> modifiedFiles, String prevVer, String curVer, final IncludeRule includeRule) { List<VcsChange> files = new ArrayList<VcsChange>(); for (ModifiedFile mf: modifiedFiles) { - String normalizedPath = normalizePath(mf.getPath()); + String normalizedPath = PathUtil.normalizeSeparator(mf.getPath()); if (!normalizedPath.startsWith(includeRule.getFrom())) continue; // skip files which do not match include rule VcsChangeInfo.Type changeType = getChangeType(mf.getStatus()); @@ -100,10 +146,6 @@ return files; } - private @NotNull String normalizePath(@NotNull String repPath) { - return repPath.replace('\\', '/'); - } - private VcsChangeInfo.Type getChangeType(final ModifiedFile.Status status) { switch (status) { case ADDED:return VcsChangeInfo.Type.ADDED; @@ -337,7 +379,8 @@ private void updateWorkingDirectory(final VcsRoot root) throws VcsException { Settings settings = new Settings(myServerPaths, root); String workDir = settings.getWorkingDir(); - synchronized (root) { + lockWorkDir(workDir); + try { if (hasRepositoryCopy(new File(workDir))) { // update PullCommand pull = new PullCommand(settings); @@ -349,9 +392,31 @@ cl.setUpdateWorkingDir(false); cl.execute(); } + } finally { + unlockWorkDir(workDir); } } + private void lockWorkDir(@NotNull String workDir) { + getWorkDirLock(workDir).lock(); + } + + private void unlockWorkDir(@NotNull String workDir) { + getWorkDirLock(workDir).unlock(); + } + + private Lock getWorkDirLock(final String workDir) { + Lock lock = myWorkDirLocks.get(workDir); + if (lock == null) { + lock = new ReentrantLock(); + Lock curLock = myWorkDirLocks.putIfAbsent(workDir, lock); + if (curLock != null) { + lock = curLock; + } + } + return lock; + } + private boolean hasRepositoryCopy(final File workDir) { // need better way to check that repository copy is ok return workDir.isDirectory() && new File(workDir, ".hg").isDirectory();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/PathUtil.java Thu Jul 17 00:44:10 2008 +0400 @@ -0,0 +1,10 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +import org.jetbrains.annotations.NotNull; + +public class PathUtil { + @NotNull + public static String normalizeSeparator(@NotNull String repPath) { + return repPath.replace('\\', '/'); + } + }
--- a/mercurial/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Settings.java Wed Jul 16 19:22:52 2008 +0400 +++ b/mercurial/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Settings.java Thu Jul 17 00:44:10 2008 +0400 @@ -1,8 +1,11 @@ package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; import jetbrains.buildServer.buildTriggers.vcs.mercurial.Constants; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.PathUtil; import jetbrains.buildServer.serverSide.ServerPaths; import jetbrains.buildServer.vcs.VcsRoot; +import jetbrains.buildServer.util.Hash; +import jetbrains.buildServer.util.FileUtil; import org.jetbrains.annotations.NotNull; import java.io.File; @@ -65,11 +68,25 @@ return myWorkingDir; } - String workingDirname = String.valueOf(myRepository.hashCode()); - File workFoldersRootDir = new File(myServerPaths.getCachesDir(), "mercurial"); + return getDefaultWorkDir(myServerPaths, myRepository); + } + + public static String DEFAULT_WORK_DIR_PREFIX = "hg_"; + + private static String getDefaultWorkDir(@NotNull ServerPaths serverPaths, @NotNull String repPath) { + String workingDirname = DEFAULT_WORK_DIR_PREFIX + String.valueOf(Hash.calc(normalize(repPath))); + File workFoldersRootDir = new File(serverPaths.getCachesDir(), "mercurial"); if (!workFoldersRootDir.mkdirs() && !workFoldersRootDir.isDirectory()) { throw new RuntimeException("Cannot create directory: " + workFoldersRootDir.getAbsolutePath()); } - return new File(workFoldersRootDir, workingDirname).getAbsolutePath(); + return FileUtil.getCanonicalFile(new File(workFoldersRootDir, workingDirname)).getAbsolutePath(); + } + + private static String normalize(final String path) { + String normalized = PathUtil.normalizeSeparator(path); + if (path.endsWith("/")) { + return normalized.substring(0, normalized.length()-1); + } + return normalized; } }