diff --git a/config/alfresco/avm-console-context.xml b/config/alfresco/avm-console-context.xml
index dc4575b7cb..bc6380bef1 100644
--- a/config/alfresco/avm-console-context.xml
+++ b/config/alfresco/avm-console-context.xml
@@ -31,7 +31,7 @@
build/storage
- true
+ false
diff --git a/source/java/org/alfresco/repo/avm/AVMCrawlTest.java b/source/java/org/alfresco/repo/avm/AVMCrawlTest.java
index d5bc66c141..8f44df13fe 100644
--- a/source/java/org/alfresco/repo/avm/AVMCrawlTest.java
+++ b/source/java/org/alfresco/repo/avm/AVMCrawlTest.java
@@ -36,10 +36,10 @@ public class AVMCrawlTest extends AVMServiceTestBase
public void testCrawl()
{
int n = 4; // Number of Threads.
- int m = 4; // How many multiples of content to start with.
+ int m = 16; // How many multiples of content to start with.
long runTime = 1200000; // Ten minutes
fService.purgeRepository("main");
- fReaper.setInactiveBaseSleep(30000);
+ fReaper.setInactiveBaseSleep(60000);
BulkLoader loader = new BulkLoader();
loader.setAvmService(fService);
for (int i = 0; i < m; i++)
diff --git a/source/java/org/alfresco/repo/avm/AVMInteractiveConsole.java b/source/java/org/alfresco/repo/avm/AVMInteractiveConsole.java
index 11c6129be5..800333e1d5 100644
--- a/source/java/org/alfresco/repo/avm/AVMInteractiveConsole.java
+++ b/source/java/org/alfresco/repo/avm/AVMInteractiveConsole.java
@@ -398,6 +398,17 @@ public class AVMInteractiveConsole
}
reader.close();
}
+ else if (command[0].equals("ca"))
+ {
+ if (command.length != 5)
+ {
+ System.err.println("Syntax error.");
+ }
+ AVMNodeDescriptor left = fService.lookup(Integer.parseInt(command[2]), command[1]);
+ AVMNodeDescriptor right = fService.lookup(Integer.parseInt(command[4]), command[3]);
+ AVMNodeDescriptor ca = fService.getCommonAncestor(left, right);
+ System.out.println(ca);
+ }
else if (command[0].equals("exit"))
{
done = true;
diff --git a/source/java/org/alfresco/repo/avm/AVMNodeDescriptor.java b/source/java/org/alfresco/repo/avm/AVMNodeDescriptor.java
index 5172f9ba28..53a39bacde 100644
--- a/source/java/org/alfresco/repo/avm/AVMNodeDescriptor.java
+++ b/source/java/org/alfresco/repo/avm/AVMNodeDescriptor.java
@@ -393,4 +393,33 @@ public class AVMNodeDescriptor
throw new AVMException("Internal Error.");
}
}
+
+ /**
+ * Equals override.
+ * @param obj
+ * @return Equality.
+ */
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ {
+ return true;
+ }
+ if (!(obj instanceof AVMNodeDescriptor))
+ {
+ return false;
+ }
+ return fID == ((AVMNodeDescriptor)obj).fID;
+ }
+
+ /**
+ * Hashcode override.
+ * @return The objid as hashcode.
+ */
+ @Override
+ public int hashCode()
+ {
+ return (int)fID;
+ }
}
diff --git a/source/java/org/alfresco/repo/avm/AVMService.java b/source/java/org/alfresco/repo/avm/AVMService.java
index 1125437b2d..ba30de6377 100644
--- a/source/java/org/alfresco/repo/avm/AVMService.java
+++ b/source/java/org/alfresco/repo/avm/AVMService.java
@@ -172,13 +172,13 @@ public interface AVMService
* semantics.
* @param repositories The names of the repositories to snapshot.
*/
- public void createSnapshot(List repositories);
+ public List createSnapshot(List repositories);
/**
* Snapshot the given repository.
* @param repository The name of the repository to snapshot.
*/
- public void createSnapshot(String repository);
+ public int createSnapshot(String repository);
/**
* Get the set of versions in a Repository
@@ -276,4 +276,17 @@ public interface AVMService
* @param path The path to the layered directory.
*/
public void setOpacity(String path, boolean opacity);
+
+ /**
+ * Get the common ancestor of two nodes if one exists.
+ * @param left The first node.
+ * @param right The second node.
+ * @return The common ancestor. There are four possible results. Null means
+ * that there is no common ancestor. Left returned means that left is strictly
+ * an ancestor of right. Right returned means that right is strictly an
+ * ancestor of left. Any other non null return is the common ancestor and
+ * indicates that left and right are in conflict.
+ */
+ public AVMNodeDescriptor getCommonAncestor(AVMNodeDescriptor left,
+ AVMNodeDescriptor right);
}
diff --git a/source/java/org/alfresco/repo/avm/AVMServiceImpl.java b/source/java/org/alfresco/repo/avm/AVMServiceImpl.java
index d54459e6fa..40659a8e95 100644
--- a/source/java/org/alfresco/repo/avm/AVMServiceImpl.java
+++ b/source/java/org/alfresco/repo/avm/AVMServiceImpl.java
@@ -533,7 +533,7 @@ public class AVMServiceImpl implements AVMService
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createSnapshot(java.util.List)
*/
- public void createSnapshot(final List repositories)
+ public List createSnapshot(final List repositories)
{
if (repositories == null)
{
@@ -541,20 +541,23 @@ public class AVMServiceImpl implements AVMService
}
class HTxnCallback implements HibernateTxnCallback
{
+ public List versionIDs;
+
public void perform(Session session)
{
fSuperRepository.setSession(session);
- fSuperRepository.createSnapshot(repositories);
+ versionIDs = fSuperRepository.createSnapshot(repositories);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
+ return doit.versionIDs;
}
/* (non-Javadoc)
* @see org.alfresco.repo.avm.AVMService#createSnapshot(java.lang.String)
*/
- public void createSnapshot(final String repository)
+ public int createSnapshot(final String repository)
{
if (repository == null)
{
@@ -562,14 +565,17 @@ public class AVMServiceImpl implements AVMService
}
class HTxnCallback implements HibernateTxnCallback
{
+ public int versionID;
+
public void perform(Session session)
{
fSuperRepository.setSession(session);
- fSuperRepository.createSnapshot(repository);
+ versionID = fSuperRepository.createSnapshot(repository);
}
}
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, true);
+ return doit.versionID;
}
/* (non-Javadoc)
@@ -906,4 +912,36 @@ public class AVMServiceImpl implements AVMService
HTxnCallback doit = new HTxnCallback();
fTransaction.perform(doit, false);
}
- }
+
+ /**
+ * Get the common ancestor of two nodes if one exists.
+ * @param left The first node.
+ * @param right The second node.
+ * @return The common ancestor. There are four possible results. Null means
+ * that there is no common ancestor. Left returned means that left is strictly
+ * an ancestor of right. Right returned means that right is strictly an
+ * ancestor of left. Any other non null return is the common ancestor and
+ * indicates that left and right are in conflict.
+ */
+ public AVMNodeDescriptor getCommonAncestor(final AVMNodeDescriptor left,
+ final AVMNodeDescriptor right)
+ {
+ if (left == null || right == null)
+ {
+ throw new AVMBadArgumentException("Null node descriptor.");
+ }
+ class HTxnCallback implements HibernateTxnCallback
+ {
+ public AVMNodeDescriptor ancestor;
+
+ public void perform(Session session)
+ {
+ fSuperRepository.setSession(session);
+ ancestor = fSuperRepository.getCommonAncestor(left, right);
+ }
+ }
+ HTxnCallback doit = new HTxnCallback();
+ fTransaction.perform(doit, false);
+ return doit.ancestor;
+ }
+}
diff --git a/source/java/org/alfresco/repo/avm/AVMServiceTest.java b/source/java/org/alfresco/repo/avm/AVMServiceTest.java
index 66df2560bf..3e2fd97685 100644
--- a/source/java/org/alfresco/repo/avm/AVMServiceTest.java
+++ b/source/java/org/alfresco/repo/avm/AVMServiceTest.java
@@ -1851,13 +1851,13 @@ public class AVMServiceTest extends AVMServiceTestBase
loader.setAvmService(fService);
loader.recursiveLoad("source/java/org/alfresco/repo/avm", "main:/");
times.add(System.currentTimeMillis());
- fService.createSnapshot("main");
+ assertEquals(1, fService.createSnapshot("main"));
loader.recursiveLoad("source/java/org/alfresco/repo/action", "main:/");
times.add(System.currentTimeMillis());
- fService.createSnapshot("main");
+ assertEquals(2, fService.createSnapshot("main"));
loader.recursiveLoad("source/java/org/alfresco/repo/audit", "main:/");
times.add(System.currentTimeMillis());
- fService.createSnapshot("main");
+ assertEquals(3, fService.createSnapshot("main"));
assertEquals(1, fService.getRepositoryVersions("main", null, new Date(times.get(0))).size());
assertEquals(3, fService.getRepositoryVersions("main", new Date(times.get(0)), null).size());
assertEquals(2, fService.getRepositoryVersions("main", new Date(times.get(1)),
@@ -1948,4 +1948,30 @@ public class AVMServiceTest extends AVMServiceTestBase
fail();
}
}
+
+ /**
+ * Test common ancestor.
+ */
+ public void testCommonAncestor()
+ {
+ try
+ {
+ setupBasicTree();
+ fService.createBranch(-1, "main:/a", "main:/", "branch");
+ fService.createSnapshot("main");
+ AVMNodeDescriptor ancestor = fService.lookup(-1, "main:/a/b/c/foo");
+ fService.getFileOutputStream("main:/a/b/c/foo").close();
+ fService.getFileOutputStream("main:/branch/b/c/foo").close();
+ fService.createSnapshot("main");
+ AVMNodeDescriptor main = fService.lookup(-1, "main:/a/b/c/foo");
+ AVMNodeDescriptor branch = fService.lookup(-1, "main:/branch/b/c/foo");
+ AVMNodeDescriptor ca = fService.getCommonAncestor(main, branch);
+ assertEquals(ancestor, ca);
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace(System.err);
+ fail();
+ }
+ }
}
diff --git a/source/java/org/alfresco/repo/avm/PurgeTest.java b/source/java/org/alfresco/repo/avm/PurgeTest.java
index 54a9cd3d5b..82e15726d2 100644
--- a/source/java/org/alfresco/repo/avm/PurgeTest.java
+++ b/source/java/org/alfresco/repo/avm/PurgeTest.java
@@ -17,7 +17,6 @@
package org.alfresco.repo.avm;
-import org.alfresco.repo.avm.hibernate.HibernateHelper;
import org.alfresco.repo.avm.util.BulkLoader;
/**
diff --git a/source/java/org/alfresco/repo/avm/Repository.java b/source/java/org/alfresco/repo/avm/Repository.java
index aceaf618f5..26ef1d39d7 100644
--- a/source/java/org/alfresco/repo/avm/Repository.java
+++ b/source/java/org/alfresco/repo/avm/Repository.java
@@ -46,8 +46,9 @@ interface Repository
* Snapshots this repository. This sets all nodes in the
* the repository to the should be copied state, and creates
* a new version root.
+ * @return The version id of the newly created snapshot.
*/
- public void createSnapshot();
+ public int createSnapshot();
/**
* Create a new directory.
diff --git a/source/java/org/alfresco/repo/avm/RepositoryImpl.java b/source/java/org/alfresco/repo/avm/RepositoryImpl.java
index e1e53a9cc2..593c05d407 100644
--- a/source/java/org/alfresco/repo/avm/RepositoryImpl.java
+++ b/source/java/org/alfresco/repo/avm/RepositoryImpl.java
@@ -125,9 +125,10 @@ class RepositoryImpl implements Repository, Serializable
/**
* Snapshot this repository. This creates a new version record.
+ * @return The version id of the new snapshot.
*/
@SuppressWarnings("unchecked")
- public void createSnapshot()
+ public int createSnapshot()
{
// If the root isn't new, we can't take a snapshot since nothing has changed.
if (!fRoot.getIsNew())
@@ -151,6 +152,7 @@ class RepositoryImpl implements Repository, Serializable
fSuper.getSession().save(versionRoot);
// Increment the version id.
fNextVersionID++;
+ return fNextVersionID - 1;
}
/**
diff --git a/source/java/org/alfresco/repo/avm/SimultaneousLoadTest.java b/source/java/org/alfresco/repo/avm/SimultaneousLoadTest.java
index 5d41fe3a9b..a07efb1597 100644
--- a/source/java/org/alfresco/repo/avm/SimultaneousLoadTest.java
+++ b/source/java/org/alfresco/repo/avm/SimultaneousLoadTest.java
@@ -33,8 +33,8 @@ public class SimultaneousLoadTest extends AVMServiceTestBase
try
{
int n = 8;
- int m = 1;
- fReaper.setInactiveBaseSleep(30000);
+ int m = 2;
+ fReaper.setInactiveBaseSleep(60000);
for (int i = 0; i < n; i++)
{
fService.createDirectory("main:/", "d" + i);
diff --git a/source/java/org/alfresco/repo/avm/SuperRepository.java b/source/java/org/alfresco/repo/avm/SuperRepository.java
index a6e22b6d50..cf79fa44fb 100644
--- a/source/java/org/alfresco/repo/avm/SuperRepository.java
+++ b/source/java/org/alfresco/repo/avm/SuperRepository.java
@@ -414,28 +414,32 @@ class SuperRepository
/**
* Snapshot the given repositories.
* @param repositories The list of repository name to snapshot.
+ * @return A List of version ids for each newly snapshotted repository.
*/
- public void createSnapshot(List repositories)
+ public List createSnapshot(List repositories)
{
+ List result = new ArrayList();
for (String repName : repositories)
{
Repository repo = getRepositoryByName(repName, true);
fSession.get().lock(repo, LockMode.UPGRADE);
// fSession.get().lock(repo, LockMode.UPGRADE);
- repo.createSnapshot();
+ result.add(repo.createSnapshot());
}
+ return result;
}
/**
* Create a snapshot of a single repository.
* @param repository The name of the repository.
+ * @return The version id of the newly snapshotted repository.
*/
- public void createSnapshot(String repository)
+ public int createSnapshot(String repository)
{
Repository repo = getRepositoryByName(repository, true);
fSession.get().lock(repo, LockMode.UPGRADE);
// fSession.get().lock(repo, LockMode.UPGRADE);
- repo.createSnapshot();
+ return repo.createSnapshot();
}
/**
@@ -890,6 +894,52 @@ class SuperRepository
return getRepositoryByName(name, false).getDescriptor();
}
+ /**
+ * Get the common ancestor of two nodes if one exists. Unfortunately
+ * this is a quadratic problem, taking time proportional to the product
+ * of the lengths of the left and right history chains.
+ * @param left The first node.
+ * @param right The second node.
+ * @return The common ancestor. There are four possible results. Null means
+ * that there is no common ancestor. Left returned means that left is strictly
+ * an ancestor of right. Right returned means that right is strictly an
+ * ancestor of left. Any other non null return is the common ancestor and
+ * indicates that left and right are in conflict.
+ */
+ public AVMNodeDescriptor getCommonAncestor(AVMNodeDescriptor left,
+ AVMNodeDescriptor right)
+ {
+ AVMNode lNode = (AVMNode)fSession.get().get(AVMNodeImpl.class, left.getId());
+ AVMNode rNode = (AVMNode)fSession.get().get(AVMNodeImpl.class, right.getId());
+ if (lNode == null || rNode == null)
+ {
+ throw new AVMNotFoundException("Node not found.");
+ }
+ List leftHistory = new ArrayList();
+ while (lNode != null)
+ {
+ leftHistory.add(lNode);
+ lNode = lNode.getAncestor();
+ }
+ List rightHistory = new ArrayList();
+ while (rNode != null)
+ {
+ rightHistory.add(rNode);
+ rNode = rNode.getAncestor();
+ }
+ for (AVMNode l : leftHistory)
+ {
+ for (AVMNode r : rightHistory)
+ {
+ if (l.equals(r))
+ {
+ return l.getDescriptor("", "", "");
+ }
+ }
+ }
+ return null;
+ }
+
/**
* Get the single instance of SuperRepository.
* @return The single instance.