changeset 372:2869f49b9211

TW-17252 handle mercurial caches correctly
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Thu, 09 Feb 2012 12:58:53 +0400
parents 24d926f22e85
children 1350f99b43bc
files mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManagerImpl.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialResetCacheHandler.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialResetCacheHandlerTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java mercurial-tests/src/testng.xml
diffstat 6 files changed, 208 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManagerImpl.java	Wed Feb 08 16:45:14 2012 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MirrorManagerImpl.java	Thu Feb 09 12:58:53 2012 +0400
@@ -77,14 +77,19 @@
 
   @NotNull
   public Map<String, File> getMappings() {
-    return new HashMap<String, File>(myMirrors);
+    myLock.readLock().lock();
+    try {
+      return new HashMap<String, File>(myMirrors);
+    } finally {
+      myLock.readLock().unlock();
+    }
   }
 
   public void lockDir(@NotNull final File dir) {
     lockFor(dir).lock();
   }
 
-  public void unlockDir(@NotNull File dir) {
+  public void unlockDir(@NotNull final File dir) {
     lockFor(dir).unlock();
   }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialResetCacheHandler.java	Thu Feb 09 12:58:53 2012 +0400
@@ -0,0 +1,88 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import com.intellij.openapi.diagnostic.Logger;
+import jetbrains.buildServer.util.cache.ResetCacheHandler;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.*;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static com.intellij.openapi.util.io.FileUtil.delete;
+import static java.util.Collections.singletonList;
+
+/**
+ * @author dmitry.neverov
+ */
+public class MercurialResetCacheHandler implements ResetCacheHandler {
+
+  private static Logger LOG = Logger.getInstance(MercurialResetCacheHandler.class.getName());
+  private static final String MERCURIAL_CACHE_NAME = "mercurial";
+
+  private final MirrorManager myMirrorManager;
+  private AtomicBoolean myResetRunning = new AtomicBoolean(false);
+
+  public MercurialResetCacheHandler(@NotNull MirrorManager mirrorManager) {
+    myMirrorManager = mirrorManager;
+  }
+
+  @NotNull
+  public List<String> listCaches() {
+    return singletonList(MERCURIAL_CACHE_NAME);
+  }
+
+  public boolean isEmpty(@NotNull final String cache) {
+    return myMirrorManager.getMappings().isEmpty();
+  }
+
+  public void resetCache(@NotNull final String cache) {
+    boolean started = startReset();
+    if (!started) {
+      LOG.info("Mercurial mirrors reset is already running");
+      return;
+    }
+    resetAllMirrors();
+    finishReset();
+  }
+
+  private boolean startReset() {
+    return myResetRunning.compareAndSet(false, true);
+  }
+
+  private void finishReset() {
+    myResetRunning.set(false);
+  }
+
+  private void resetAllMirrors() {
+    LOG.info("Start reseting mercurial caches");
+    for (Map.Entry<String, File> entry : myMirrorManager.getMappings().entrySet()) {
+      String url = entry.getKey();
+      File mirror = entry.getValue();
+      try {
+        lockMirror(url, mirror);
+        resetMirror(mirror);
+      } finally {
+        unlockMirror(url, mirror);
+      }
+    }
+    LOG.info("Mercurial caches reseted");
+  }
+
+  private void lockMirror(@NotNull final String url, @NotNull final File mirror) {
+    LOG.debug("Lock mirror of " + url);
+    myMirrorManager.lockDir(mirror);
+    LOG.debug("Mirror of " + url + " is locked");
+  }
+
+  private void resetMirror(@NotNull final File mirror) {
+    LOG.debug("Reset mercurial mirror "  + mirror.getAbsolutePath());
+    myMirrorManager.forgetDir(mirror);
+    delete(mirror);
+    LOG.debug("Mercurial mirror "  + mirror.getAbsolutePath() + " reseted");
+  }
+
+  private void unlockMirror(@NotNull final String url, @NotNull final File mirror) {
+    myMirrorManager.unlockDir(mirror);
+    LOG.debug("Mirror of " + url + " is unlocked");
+  }
+}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Wed Feb 08 16:45:14 2012 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Thu Feb 09 12:58:53 2012 +0400
@@ -24,6 +24,7 @@
 import jetbrains.buildServer.util.EventDispatcher;
 import jetbrains.buildServer.util.FileUtil;
 import jetbrains.buildServer.util.StringUtil;
+import jetbrains.buildServer.util.cache.ResetCacheRegister;
 import jetbrains.buildServer.vcs.*;
 import jetbrains.buildServer.vcs.impl.VcsRootImpl;
 import jetbrains.buildServer.vcs.patches.PatchBuilder;
@@ -63,6 +64,7 @@
   public MercurialVcsSupport(@NotNull final VcsManager vcsManager,
                              @NotNull final SBuildServer server,
                              @NotNull final EventDispatcher<BuildServerListener> dispatcher,
+                             @NotNull final ResetCacheRegister resetCacheHandlerManager,
                              @NotNull final ServerPluginConfig config,
                              @NotNull final HgPathProvider hgPathProvider,
                              @NotNull final CommandFactory commandFactory,
@@ -73,6 +75,7 @@
     myMirrorManager = mirrorManager;
     myHgPathProvider = hgPathProvider;
     myCommandFactory = commandFactory;
+    resetCacheHandlerManager.registerHandler(new MercurialResetCacheHandler(myMirrorManager));
     dispatcher.addListener(new BuildServerAdapter() {
       @Override
       public void cleanupFinished() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialResetCacheHandlerTest.java	Thu Feb 09 12:58:53 2012 +0400
@@ -0,0 +1,107 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.TempFiles;
+import jetbrains.buildServer.util.cache.ResetCacheHandler;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.States;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.emptyMap;
+import static org.testng.AssertJUnit.*;
+
+/**
+ * @author dmitry.neverov
+ */
+@Test
+public class MercurialResetCacheHandlerTest {
+
+  private Mockery myContext;
+  private TempFiles myTempFiles;
+  private File myCachesDir;
+  private MirrorManager myMirrorManager;
+  private ResetCacheHandler myCacheHandler;
+
+  @BeforeMethod
+  public void setUp() throws IOException {
+    myContext = new Mockery();
+    myTempFiles = new TempFiles();
+    myCachesDir = myTempFiles.createTempDir();
+    myMirrorManager = myContext.mock(MirrorManager.class);
+    myCacheHandler = new MercurialResetCacheHandler(myMirrorManager);
+  }
+
+  @AfterMethod
+  public void tearDown() {
+    myTempFiles.cleanup();
+  }
+
+
+  public void cache_list_should_contains_entry_for_mercurial_in_general() {
+    assertEquals(asList("mercurial"), myCacheHandler.listCaches());
+  }
+
+
+  public void mercurial_cache_is_empty_when_there_are_no_mirrors() {
+    myContext.checking(new Expectations() {{
+      atLeast(1).of(myMirrorManager).getMappings(); will(returnValue(emptyMap()));
+    }});
+
+    assertTrue(myCacheHandler.isEmpty("mercurial"));
+    myContext.assertIsSatisfied();
+  }
+
+
+  public void mercurial_cache_is_not_empty_when_mirrors_exist() {
+    final Map<String, File> mapping = new HashMap<String, File>() {{
+      put("http://some.org/repository1", new File(myCachesDir, "a"));
+      put("http://some.org/repository2", new File(myCachesDir, "b"));
+    }};
+    myContext.checking(new Expectations() {{
+      atLeast(1).of(myMirrorManager).getMappings(); will(returnValue(mapping));
+    }});
+
+    assertFalse(myCacheHandler.isEmpty("mercurial"));
+    myContext.assertIsSatisfied();
+  }
+
+
+  public void reset_cache_should_reset_caches_for_all_mirrors() {
+    final String url1 = "http://some.org/repository1";
+    final String url2 = "http://some.org/repository2";
+    final File mirror1 = new File(myCachesDir, "a");
+    final File mirror2 = new File(myCachesDir, "b");
+    mirror1.mkdirs();
+    mirror2.mkdirs();
+    final Map<String, File> mapping = new HashMap<String, File>() {{
+      put(url1, mirror1);
+      put(url2, mirror2);
+    }};
+    final States stateOfMirror1 = myContext.states("mirror1").startsAs("initial");
+    final States stateOfMirror2 = myContext.states("mirror2").startsAs("initial");
+    myContext.checking(new Expectations() {{
+      atLeast(1).of(myMirrorManager).getMappings(); will(returnValue(mapping));
+
+      one(myMirrorManager).lockDir(mirror1); when(stateOfMirror1.is("initial")); then(stateOfMirror1.is("locked"));
+      one(myMirrorManager).lockDir(mirror2); when(stateOfMirror2.is("initial")); then(stateOfMirror2.is("locked"));
+
+      one(myMirrorManager).forgetDir(mirror1); when(stateOfMirror1.is("locked")); then(stateOfMirror1.is("reseted"));
+      one(myMirrorManager).forgetDir(mirror2); when(stateOfMirror2.is("locked")); then(stateOfMirror2.is("reseted"));
+
+      one(myMirrorManager).unlockDir(mirror1); when(stateOfMirror1.is("reseted"));
+      one(myMirrorManager).unlockDir(mirror2); when(stateOfMirror2.is("reseted"));
+    }});
+
+    myCacheHandler.resetCache("mercurial");
+    myContext.assertIsSatisfied();
+    assertTrue(myCachesDir.listFiles().length == 0);
+  }
+}
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java	Wed Feb 08 16:45:14 2012 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java	Thu Feb 09 12:58:53 2012 +0400
@@ -3,6 +3,7 @@
 import jetbrains.buildServer.serverSide.BuildServerListener;
 import jetbrains.buildServer.serverSide.SBuildServer;
 import jetbrains.buildServer.util.EventDispatcher;
+import jetbrains.buildServer.util.cache.ResetCacheRegister;
 import jetbrains.buildServer.vcs.VcsManager;
 import org.jetbrains.annotations.NotNull;
 import org.jmock.Expectations;
@@ -43,6 +44,6 @@
       allowing(server).getExecutor(); will(returnValue(executor));
     }});
     EventDispatcher<BuildServerListener> dispatcher = EventDispatcher.create(BuildServerListener.class);
-    return new MercurialVcsSupport(vcsManager, server, dispatcher, config, new ServerHgPathProvider(config), commandFactory, new MirrorManagerImpl(config));
+    return new MercurialVcsSupport(vcsManager, server, dispatcher, new ResetCacheRegister(), config, new ServerHgPathProvider(config), commandFactory, new MirrorManagerImpl(config));
   }
 }
--- a/mercurial-tests/src/testng.xml	Wed Feb 08 16:45:14 2012 +0400
+++ b/mercurial-tests/src/testng.xml	Thu Feb 09 12:58:53 2012 +0400
@@ -21,6 +21,7 @@
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.DagFeaturesTest"/>
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.UnrelatedResitoriesTest"/>
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.CleanupTest"/>
+      <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialResetCacheHandlerTest"/>
     </classes>
   </test>
 </suite>