/* * Copyright (C) 2005-2010 Alfresco Software Limited. * * This file is part of Alfresco * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see . */ package org.alfresco.repo.avm; import java.util.List; import java.util.SortedMap; import javax.transaction.UserTransaction; import org.alfresco.repo.transaction.RetryingTransactionHelper.RetryingTransactionCallback; import org.alfresco.service.cmr.avm.AVMNodeDescriptor; import org.alfresco.service.cmr.avmsync.AVMDifference; import org.alfresco.service.cmr.repository.StoreRef; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.ResultSetRow; import org.alfresco.service.cmr.search.SearchService; /** * AVM concurrency and search * * @author andyh, janv */ public class AVMServiceConcurrentTest extends AVMServiceTestBase { public void testSetup() throws Exception { super.testSetup(); } public void test_CreateDelete() throws Exception { int threads= 4; int loops = 10; int snapshotsPerLoop = 4; assertEquals(1, fService.getStoreVersions("main").size()); fService.createDirectory("main:/", "test"); int startVersion = fService.createSnapshot("main", null, null).get("main"); assertEquals(2, fService.getStoreVersions("main").size()); assertEquals(0, fService.getDirectoryListing(-1, "main:/test").size()); UserTransaction testTX = fTransactionService.getUserTransaction(); testTX.begin(); StoreRef storeRef = AVMNodeConverter.ToStoreRef("main"); SearchService searchService = fIndexerAndSearcher.getSearcher(AVMNodeConverter.ToStoreRef("main"), true); ResultSet results = searchService.query(storeRef, "lucene", "PATH:\"/test/*\""); assertEquals(0, results.length()); results.close(); testTX.commit(); Thread runner = null; for (int i = 0; i < threads; i++) { runner = new Nester("Concurrent-" + i, runner, false, snapshotsPerLoop, Nester.Mode.CREATE, loops); } if (runner != null) { runner.start(); try { runner.join(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Snapshot count: "+fService.getStoreVersions("main").size()); SortedMap listing = fService.getDirectoryListing(-1, "main:/test"); assertEquals(loops, listing.size()); for(AVMNodeDescriptor node : listing.values()) { System.out.println("Listed: "+node.getPath()+" "+node.getVersionID()); } List diffs = fSyncService.compare(startVersion, "main:/", -1, "main:/", null); assertEquals(loops, diffs.size()); for(AVMDifference diff : diffs) { AVMNodeDescriptor desc = fService.lookup(diff.getDestinationVersion(), diff.getDestinationPath(), true); assertFalse(desc.isDeleted()); } testTX = fTransactionService.getUserTransaction(); testTX.begin(); try { searchService = fIndexerAndSearcher.getSearcher(AVMNodeConverter.ToStoreRef("main"), true); results = searchService.query(storeRef, "lucene", "PATH:\"/test/*\""); for(ResultSetRow row : results) { System.out.println("Found: "+row.getNodeRef()); } assertEquals(loops, results.length()); results.close(); } finally { try { testTX.commit(); } catch (Exception e) {} } // delete runner = null; for (int i = 0; i < threads; i++) { runner = new Nester("Concurrent-" + i, runner, false, snapshotsPerLoop, Nester.Mode.DELETE, loops); } if (runner != null) { runner.start(); try { runner.join(); } catch (InterruptedException e) { e.printStackTrace(); } } assertEquals(0, fService.getDirectoryListing(-1, "main:/test").size()); System.out.println("Snapshot count: "+fService.getStoreVersions("main").size()); /* for(org.alfresco.service.cmr.avm.VersionDescriptor v : fService.getStoreVersions("main")) { System.out.println(v); } */ testTX = fTransactionService.getUserTransaction(); testTX.begin(); searchService = fIndexerAndSearcher.getSearcher(AVMNodeConverter.ToStoreRef("main"), true); results = searchService.query(storeRef, "lucene", "PATH:\"/test/*\""); for(ResultSetRow row : results) { System.out.println("Found: "+row.getNodeRef()); } assertEquals(0, results.length()); results.close(); testTX.commit(); } public void test_ALF_786() throws Exception { int threads= 4; int loops = 10; int snapshotsPerLoop = 4; fService.createDirectory("main:/", "test"); int startVersion = fService.createSnapshot("main", null, null).get("main"); assertEquals(0, fService.getDirectoryListing(-1, "main:/test").size()); UserTransaction testTX = fTransactionService.getUserTransaction(); testTX.begin(); StoreRef storeRef = AVMNodeConverter.ToStoreRef("main"); SearchService searchService = fIndexerAndSearcher.getSearcher(AVMNodeConverter.ToStoreRef("main"), true); ResultSet results = searchService.query(storeRef, "lucene", "PATH:\"/test/*\""); assertEquals(0, results.length()); results.close(); testTX.commit(); // create Thread runner = null; for (int i = 0; i < threads; i++) { runner = new Nester("Concurrent-" + i, runner, false, snapshotsPerLoop, Nester.Mode.CREATE, loops); } if (runner != null) { runner.start(); try { runner.join(); } catch (InterruptedException e) { e.printStackTrace(); } } SortedMap listing = fService.getDirectoryListing(-1, "main:/test"); assertEquals(loops, listing.size()); for(AVMNodeDescriptor node : listing.values()) { System.out.println("Listed: "+node.getPath()+" "+node.getVersionID()); } List diffs = fSyncService.compare(startVersion, "main:/", -1, "main:/", null); assertEquals(loops, diffs.size()); for(AVMDifference diff : diffs) { AVMNodeDescriptor desc = fService.lookup(diff.getDestinationVersion(), diff.getDestinationPath(), true); assertFalse(desc.isDeleted()); } testTX = fTransactionService.getUserTransaction(); testTX.begin(); try { searchService = fIndexerAndSearcher.getSearcher(AVMNodeConverter.ToStoreRef("main"), true); results = searchService.query(storeRef, "lucene", "PATH:\"/test/*\""); for(ResultSetRow row : results) { System.out.println("Found: "+row.getNodeRef()); } assertEquals(loops, results.length()); results.close(); } finally { try { testTX.commit(); } catch (Exception e) {} } // update runner = null; for (int i = 0; i < threads; i++) { runner = new Nester("Concurrent-" + i, runner, false, snapshotsPerLoop, Nester.Mode.UPDATE, loops); } if (runner != null) { runner.start(); try { runner.join(); } catch (InterruptedException e) { e.printStackTrace(); } } testTX = fTransactionService.getUserTransaction(); testTX.begin(); searchService = fIndexerAndSearcher.getSearcher(AVMNodeConverter.ToStoreRef("main"), true); results = searchService.query(storeRef, "lucene", "PATH:\"/test/*\""); for(ResultSetRow row : results) { System.out.println("Found: "+row.getNodeRef()); } assertEquals(loops, results.length()); results.close(); testTX.commit(); // delete runner = null; for (int i = 0; i < threads; i++) { runner = new Nester("Concurrent-" + i, runner, false, snapshotsPerLoop, Nester.Mode.DELETE, loops); } if (runner != null) { runner.start(); try { runner.join(); } catch (InterruptedException e) { e.printStackTrace(); } } assertEquals(0, fService.getDirectoryListing(-1, "main:/test").size()); testTX = fTransactionService.getUserTransaction(); testTX.begin(); searchService = fIndexerAndSearcher.getSearcher(AVMNodeConverter.ToStoreRef("main"), true); results = searchService.query(storeRef, "lucene", "PATH:\"/test/*\""); for(ResultSetRow row : results) { System.out.println("Found: "+row.getNodeRef()); } assertEquals(0, results.length()); results.close(); testTX.commit(); // recreate runner = null; for (int i = 0; i < threads; i++) { runner = new Nester("Concurrent-" + i, runner, false, snapshotsPerLoop, Nester.Mode.CREATE, loops); } if (runner != null) { runner.start(); try { runner.join(); } catch (InterruptedException e) { e.printStackTrace(); } } testTX = fTransactionService.getUserTransaction(); testTX.begin(); searchService = fIndexerAndSearcher.getSearcher(AVMNodeConverter.ToStoreRef("main"), true); results = searchService.query(storeRef, "lucene", "PATH:\"/test/*\""); for(ResultSetRow row : results) { System.out.println("Found: "+row.getNodeRef()); } assertEquals(loops, results.length()); results.close(); testTX.commit(); // move runner = null; for (int i = 0; i < threads; i++) { runner = new Nester("Concurrent-" + i, runner, false, snapshotsPerLoop, Nester.Mode.MOVE, loops); } if (runner != null) { runner.start(); try { runner.join(); } catch (InterruptedException e) { e.printStackTrace(); } } testTX = fTransactionService.getUserTransaction(); testTX.begin(); searchService = fIndexerAndSearcher.getSearcher(AVMNodeConverter.ToStoreRef("main"), true); results = searchService.query(storeRef, "lucene", "PATH:\"/test/*\""); for(ResultSetRow row : results) { System.out.println("Found: "+row.getNodeRef()); } assertEquals(loops, results.length()); results.close(); testTX.commit(); } public void xtest_ALF_786_PLUS() throws Exception { int startVersion; UserTransaction testTX = fTransactionService.getUserTransaction(); testTX.begin(); fService.createDirectory("main:/", "test"); startVersion = fService.createSnapshot("main", null, null).get("main"); testTX.commit(); testTX = fTransactionService.getUserTransaction(); testTX.begin(); StoreRef storeRef = AVMNodeConverter.ToStoreRef("main"); SearchService searchService = fIndexerAndSearcher.getSearcher(AVMNodeConverter.ToStoreRef("main"), true); ResultSet results = searchService.query(storeRef, "lucene", "PATH:\"/test/*\""); assertEquals(0, results.length()); results.close(); testTX.commit(); Thread runner = null; for (int i = 0; i < 10; i++) { runner = new Nester("Concurrent-" + i, runner, true, 10, Nester.Mode.CREATE, 10 ); } if (runner != null) { runner.start(); try { runner.join(); } catch (InterruptedException e) { e.printStackTrace(); } } testTX = fTransactionService.getUserTransaction(); testTX.begin(); // snap testTX.commit(); testTX = fTransactionService.getUserTransaction(); testTX.begin();; SortedMap listing = fService.getDirectoryListing(-1, "main:/test"); assertEquals(100, listing.size()); for(AVMNodeDescriptor node : listing.values()) { System.out.println("Listed: "+node.getPath()+" "+node.getVersionID()); } List diffs = fSyncService.compare(startVersion, "main:/", -1, "main:/", null); assertEquals(100, diffs.size()); for(AVMDifference diff : diffs) { AVMNodeDescriptor desc = fService.lookup(diff.getDestinationVersion(), diff.getDestinationPath(), true); assertFalse(desc.isDeleted()); } searchService = fIndexerAndSearcher.getSearcher(AVMNodeConverter.ToStoreRef("main"), true); results = searchService.query(storeRef, "lucene", "PATH:\"/test/*\""); for(ResultSetRow row : results) { System.out.println("Found: "+row.getNodeRef()); } assertEquals(100, results.length()); results.close(); testTX.commit(); } static class Nester extends Thread { enum Mode {CREATE, UPDATE, DELETE, MOVE}; Thread waiter; int i; boolean multiThread; int snapshotCount; Mode mode; int loopCount; Nester(String name, Thread waiter, boolean multiThread, int snapshotCount, Mode mode, int loopCount) { super(name); this.setDaemon(true); this.waiter = waiter; this.multiThread = multiThread; this.snapshotCount = snapshotCount; this.mode = mode; this.loopCount = loopCount; } public void run() { fAuthenticationComponent.setSystemUserAsCurrentUser(); if (waiter != null) { waiter.start(); } try { //System.out.println("Start " + this.getName()); for(i = 0; i < loopCount; i++) { RetryingTransactionCallback create = new RetryingTransactionCallback() { public Void execute() throws Throwable { System.out.println("Create file: " + "main:/test/" + getName()+"-"+i); fService.createFile("main:/test", getName()+"-"+i).close(); return null; } }; RetryingTransactionCallback update = new RetryingTransactionCallback() { public Void execute() throws Throwable { System.out.println("Update file mime type: " + "main:/test/" + getName()+"-"+i); fService.setMimeType("main:/test/"+getName()+"-"+i, "text/plain"); return null; } }; RetryingTransactionCallback delete = new RetryingTransactionCallback() { public Void execute() throws Throwable { System.out.println("Remove file: " + "main:/test/" + getName()+"-"+i); fService.removeNode("main:/test/"+getName()+"-"+i); return null; } }; RetryingTransactionCallback move = new RetryingTransactionCallback() { public Void execute() throws Throwable { System.out.println("Rename file: " + "main:/test/" + getName()+"-"+i); fService.rename("main:/test/", getName()+"-"+i, "main:/test/", "MOVED-"+getName()+"-"+i); return null; } }; if(multiThread || (waiter == null)) { // only one thread creates for 786 switch(mode) { case CREATE: fRetryingTransactionHelper.doInTransaction(create); break; case UPDATE: fRetryingTransactionHelper.doInTransaction(update); break; case DELETE: fRetryingTransactionHelper.doInTransaction(delete); break; case MOVE: fRetryingTransactionHelper.doInTransaction(move); break; default: } } RetryingTransactionCallback snap = new RetryingTransactionCallback() { public Void execute() throws Throwable { //System.out.println("Snap: main:/"); fService.createSnapshot("main", null, null); return null; } }; for(int s = 0; s < snapshotCount; s++) { fRetryingTransactionHelper.doInTransaction(snap); } } //System.out.println("End " + this.getName()); } catch (Exception e) { System.out.println("End " + this.getName() + " with error " + e.getMessage()); e.printStackTrace(); } finally { fAuthenticationComponent.clearCurrentSecurityContext(); } if (waiter != null) { try { waiter.join(); System.out.println("Waited for " + waiter.getName()+" by "+this.getName()); } catch (InterruptedException e) { } } } } }