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.