Return-Path: Delivered-To: apmail-sling-commits-archive@www.apache.org Received: (qmail 84464 invoked from network); 1 Sep 2009 13:51:03 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 1 Sep 2009 13:51:03 -0000 Received: (qmail 69285 invoked by uid 500); 1 Sep 2009 13:51:02 -0000 Delivered-To: apmail-sling-commits-archive@sling.apache.org Received: (qmail 69231 invoked by uid 500); 1 Sep 2009 13:51:02 -0000 Mailing-List: contact commits-help@sling.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@sling.apache.org Delivered-To: mailing list commits@sling.apache.org Received: (qmail 69222 invoked by uid 99); 1 Sep 2009 13:51:02 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 01 Sep 2009 13:51:02 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Tue, 01 Sep 2009 13:50:52 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 069DC23888D6; Tue, 1 Sep 2009 13:50:31 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r810055 - in /sling/trunk/installer/jcr/jcrinstall/src: main/java/org/apache/sling/jcr/jcrinstall/impl/ test/java/org/apache/sling/jcr/jcrinstall/impl/ Date: Tue, 01 Sep 2009 13:50:30 -0000 To: commits@sling.apache.org From: bdelacretaz@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20090901135031.069DC23888D6@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: bdelacretaz Date: Tue Sep 1 13:50:30 2009 New Revision: 810055 URL: http://svn.apache.org/viewvc?rev=810055&view=rev Log: SLING-1078 - correctly handle deletion and moving of root folders Added: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java (with props) sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java (with props) Modified: sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolder.java sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ResourceDetectionTest.java sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java Modified: sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java?rev=810055&r1=810054&r2=810055&view=diff ============================================================================== --- sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java (original) +++ sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.java Tue Sep 1 13:50:30 2009 @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; @@ -28,6 +29,9 @@ import javax.jcr.NodeIterator; import javax.jcr.RepositoryException; import javax.jcr.Session; +import javax.jcr.observation.Event; +import javax.jcr.observation.EventIterator; +import javax.jcr.observation.EventListener; import org.apache.sling.commons.osgi.OsgiUtil; import org.apache.sling.jcr.api.SlingRepository; @@ -55,7 +59,7 @@ * name="service.vendor" * value="The Apache Software Foundation" */ -public class JcrInstaller implements Runnable { +public class JcrInstaller implements Runnable, EventListener { public static final long RUN_LOOP_DELAY_MSEC = 500L; public static final String URL_SCHEME = "jcrinstall"; @@ -122,6 +126,12 @@ /** Used to stop background thread when deactivated */ private int deactivationCounter = 1; + /** The root folders that we watch */ + private String [] roots; + + /** Path of newly created root folders */ + private Set newRoots = new HashSet(); + /** Convert Nodes to InstallableResources */ static interface NodeConverter { InstallableResource convertNode(String urlScheme, Node n) throws Exception; @@ -141,13 +151,14 @@ log.info("activate()"); session = repository.loginAdministrative(repository.getDefaultWorkspace()); + newRoots.clear(); // Setup converters converters.add(new FileNodeConverter()); converters.add(new ConfigNodeConverter()); // Get search paths, and make sure each part starts and ends with a / - String [] roots = OsgiUtil.toStringArray(context.getProperties().get(PROP_SEARCH_PATH)); + roots = OsgiUtil.toStringArray(context.getProperties().get(PROP_SEARCH_PATH)); if (roots == null) { roots = DEFAULT_SEARCH_PATH; } @@ -162,6 +173,15 @@ for(int i = 0; i < roots.length; i++) { log.info("Configured root folder: {}", roots[i]); } + + // Watch for DELETE events on the root - that might be one of our root folders + int eventTypes = Event.NODE_ADDED | Event.NODE_REMOVED; + boolean isDeep = false; + boolean noLocal = true; + session.getWorkspace().getObservationManager().addEventListener(this, eventTypes, "/", + isDeep, null, null, noLocal); + log.info("Watching for NODE_REMOVED events on / to detect removal of our root folders"); + // Configurable max depth, system property (via bundle context) overrides default value Object obj = getPropertyValue(context, PROP_INSTALL_FOLDER_MAX_DEPTH); @@ -213,7 +233,7 @@ protected void deactivate(ComponentContext context) { log.info("deactivate()"); - + try { deactivationCounter++; folderNameFilter = null; @@ -223,10 +243,12 @@ for(RootFolderListener wfc : listeners) { wfc.cleanup(session); } + session.getWorkspace().getObservationManager().removeEventListener(this); session.logout(); session = null; } listeners.clear(); + newRoots.clear(); } catch(Exception e) { log.warn("Exception in deactivate()", e); } @@ -295,12 +317,45 @@ return path; } + /** Add WatchedFolder to our list if it doesn't exist yet */ + private void addWatchedFolder(WatchedFolder toAdd) { + WatchedFolder existing = null; + for(WatchedFolder wf : watchedFolders) { + if(wf.getPath().equals(toAdd.getPath())) { + existing = wf; + break; + } + } + if(existing == null) { + watchedFolders.add(toAdd); + toAdd.scheduleScan(); + } + } + /** Add new folders to watch if any have been detected * @return a list of InstallableResource that must be unregistered, * for folders that have been removed */ private List updateFoldersList() throws Exception { final List result = new LinkedList(); + + // If one of our root folders was just created, scan it for folders to watch + if(newRoots.size() > 0) { + final Set toScan = new HashSet(); + synchronized (newRoots) { + toScan.addAll(newRoots); + newRoots.clear(); + } + final List newFolders = new ArrayList(); + for(String root : toScan) { + findPathsToWatch(root, newFolders); + } + for(WatchedFolder wf : newFolders) { + addWatchedFolder(wf); + } + } + + // If changed occured in our watched paths, rescan for(RootFolderListener wfc : listeners) { final Set changedPaths = wfc.getAndClearPaths(); if(changedPaths != null && changedPaths.size() > 0) { @@ -308,22 +363,13 @@ for(String path : changedPaths) { // Deletions are handled below if(folderNameFilter.getPriority(path) > 0 && session.itemExists(path)) { - WatchedFolder existing = null; - for(WatchedFolder wf : watchedFolders) { - if(wf.getPath().equals(path)) { - existing = wf; - break; - } - } - if(existing == null) { - watchedFolders.add( - new WatchedFolder(session, path, folderNameFilter.getPriority(path), URL_SCHEME, converters)); - } + addWatchedFolder(new WatchedFolder(session, path, folderNameFilter.getPriority(path), URL_SCHEME, converters)); } } } } + // Check all WatchedFolder, in case some were deleted final List toRemove = new ArrayList(); for(WatchedFolder wf : watchedFolders) { if(!session.itemExists(wf.getPath())) { @@ -340,6 +386,31 @@ return result; } + public void onEvent(EventIterator it) { + // Got a DELETE on root - schedule folders rescan if one + // of our root folders is impacted + try { + while(it.hasNext()) { + final Event e = it.nextEvent(); + for(String root : roots) { + if(root.startsWith(e.getPath())) { + if(e.getType() == Event.NODE_ADDED) { + synchronized (newRoots) { + newRoots.add(e.getPath()); + } + log.info("Got create event for root {}, scheduling scanning of new folders", root); + } else { + log.info("Got delete event for root {}, scheduling folders rescan", root); + } + updateFoldersListTimer.scheduleScan(); + } + } + } + } catch(RepositoryException re) { + log.warn("RepositoryException in onEvent", re); + } + } + /** Run periodic scans of our watched folders, and watch for folders creations/deletions */ public void run() { log.info("Background thread {} starting", Thread.currentThread().getName()); Modified: sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolder.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolder.java?rev=810055&r1=810054&r2=810055&view=diff ============================================================================== --- sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolder.java (original) +++ sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolder.java Tue Sep 1 13:50:30 2009 @@ -105,9 +105,14 @@ /** Set a static "timer" whenever an event occurs */ public void onEvent(EventIterator it) { - rescanTimer.scheduleScan(); - needsScan = true; - log.debug("Event received, scheduling scan of {}", path); + log.debug("JCR event received for path {}", path); + scheduleScan(); + } + + void scheduleScan() { + log.debug("Scheduling scan of {}", path); + rescanTimer.scheduleScan(); + needsScan = true; } boolean needsScan() { Added: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java?rev=810055&view=auto ============================================================================== --- sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java (added) +++ sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java Tue Sep 1 13:50:30 2009 @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.jcr.jcrinstall.impl; + +/** Test that changes in folders to watch are correctly detected, + * including when root folders are created or deleted + */ +public class FolderDetectionTest extends JcrInstallTestBase { + + protected boolean needsTestContent() { + return false; + } + + public void testCreateAndDeleteLibs() throws Exception { + final String res = "/libs/foo/install/somefile.jar"; + assertRegistered("Before test", res, false); + + assertFalse("/libs must not exist when test starts", session.itemExists("/libs")); + contentHelper.createFolder("/libs"); + contentHelper.createFolder("/libs/foo"); + contentHelper.createFolder("/libs/foo/install"); + MiscUtil.waitAfterContentChanges(eventHelper, installer); + + contentHelper.createOrUpdateFile(res); + MiscUtil.waitAfterContentChanges(eventHelper, installer); + + assertRegistered("After creating libs and test file", res, true); + + contentHelper.delete("/libs"); + MiscUtil.waitAfterContentChanges(eventHelper, installer); + assertRegistered("After deleting libs", res, false); + } + + public void testMoveLibsToFoo() throws Exception { + final String res = "/libs/foo/install/somefile.jar"; + assertRegistered("Before test", res, false); + + assertFalse("/libs must not exist when test starts", session.itemExists("/libs")); + contentHelper.createFolder("/libs"); + contentHelper.createFolder("/libs/foo"); + contentHelper.createFolder("/libs/foo/install"); + MiscUtil.waitAfterContentChanges(eventHelper, installer); + + contentHelper.createOrUpdateFile(res); + MiscUtil.waitAfterContentChanges(eventHelper, installer); + + assertRegistered("After creating libs and test file", res, true); + + session.move("/libs", "/foo"); + MiscUtil.waitAfterContentChanges(eventHelper, installer); + assertRegistered("After moving /libs to /foo", res, false); + } + + public void testMoveLibsToApps() throws Exception { + final String res = "/libs/foo/install/somefile.jar"; + final String appsRes = "/apps/foo/install/somefile.jar"; + assertRegistered("Before test", res, false); + + assertFalse("/libs must not exist when test starts", session.itemExists("/libs")); + contentHelper.createFolder("/libs"); + contentHelper.createFolder("/libs/foo"); + contentHelper.createFolder("/libs/foo/install"); + MiscUtil.waitAfterContentChanges(eventHelper, installer); + + contentHelper.createOrUpdateFile(res); + MiscUtil.waitAfterContentChanges(eventHelper, installer); + + assertRegistered("After creating libs and test file", res, true); + + session.move("/libs", "/apps"); + MiscUtil.waitAfterContentChanges(eventHelper, installer); + MiscUtil.waitAfterContentChanges(eventHelper, installer); + assertRegistered("/apps resource must be registered", appsRes, true); + assertRegistered("/libs resource must be gone", res, false); + } +} Propchange: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FolderDetectionTest.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev URL Added: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java?rev=810055&view=auto ============================================================================== --- sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java (added) +++ sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java Tue Sep 1 13:50:30 2009 @@ -0,0 +1,102 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.jcr.jcrinstall.impl; + +import javax.jcr.Session; + +import org.apache.sling.commons.testing.jcr.RepositoryTestBase; +import org.apache.sling.jcr.api.SlingRepository; + +/** Base test class with common utilities */ +abstract class JcrInstallTestBase extends RepositoryTestBase { + public static final long TIMEOUT = 5000L; + + SlingRepository repo; + Session session; + protected EventHelper eventHelper; + protected ContentHelper contentHelper; + protected JcrInstaller installer; + protected MockOsgiInstaller osgiInstaller; + + @Override + protected void setUp() throws Exception { + super.setUp(); + repo = getRepository(); + session = repo.loginAdministrative(repo.getDefaultWorkspace()); + eventHelper = new EventHelper(session); + contentHelper = new ContentHelper(session); + contentHelper.cleanupContent(); + if(needsTestContent()) { + contentHelper.setupContent(); + } + osgiInstaller = new MockOsgiInstaller(); + installer = MiscUtil.getJcrInstaller(repo, osgiInstaller); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + contentHelper.cleanupContent(); + session.logout(); + eventHelper = null; + contentHelper = null; + installer.deactivate(MiscUtil.getMockComponentContext()); + MiscUtil.waitForInstallerThread(installer, TIMEOUT); + } + + protected abstract boolean needsTestContent(); + + protected void assertRegisteredPaths(String [] paths) { + for(String path : paths) { + assertRegistered(path, !path.contains("NOT")); + } + } + + protected void assertRegistered(String path, boolean registered) { + assertRegistered(null, path, registered); + } + + protected void assertRegistered(String info, String path, boolean registered) { + if(info == null) { + info = ""; + } else { + info += ": "; + } + + if(registered) { + assertTrue(info + "Expected " + path + " to be registered", + osgiInstaller.isRegistered(JcrInstaller.URL_SCHEME, path)); + } else { + assertFalse(info + "Expected " + path + " to be unregistered", + osgiInstaller.isRegistered(JcrInstaller.URL_SCHEME, path)); + } + } + + protected void assertRecordedCall(String action, String path) { + final String callStr = action + ":" + JcrInstaller.URL_SCHEME + ":" + path; + boolean found = false; + for(String call : osgiInstaller.getRecordedCalls()) { + if(call.startsWith(callStr)) { + found = true; + break; + } + } + assertTrue("Expecting '" + callStr + "' in recorded calls (" + osgiInstaller.getRecordedCalls() + ")", found); + } +} Propchange: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstallTestBase.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev URL Modified: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ResourceDetectionTest.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ResourceDetectionTest.java?rev=810055&r1=810054&r2=810055&view=diff ============================================================================== --- sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ResourceDetectionTest.java (original) +++ sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ResourceDetectionTest.java Tue Sep 1 13:50:30 2009 @@ -19,82 +19,24 @@ package org.apache.sling.jcr.jcrinstall.impl; import javax.jcr.Node; -import javax.jcr.Session; -import org.apache.sling.commons.testing.jcr.RepositoryTestBase; -import org.apache.sling.jcr.api.SlingRepository; import org.osgi.service.component.ComponentContext; /** Test that added/updated/removed resources are * correctly translated to OsgiInstaller registration * calls. */ -public class ResourceDetectionTest extends RepositoryTestBase { - public static final long TIMEOUT = 5000L; - SlingRepository repo; - Session session; - private EventHelper eventHelper; - private ContentHelper contentHelper; - private JcrInstaller installer; - private MockOsgiInstaller osgiInstaller; +public class ResourceDetectionTest extends JcrInstallTestBase { - @Override - protected void setUp() throws Exception { - super.setUp(); - repo = getRepository(); - session = repo.loginAdministrative(repo.getDefaultWorkspace()); - eventHelper = new EventHelper(session); - contentHelper = new ContentHelper(session); - contentHelper.cleanupContent(); - contentHelper.setupContent(); - osgiInstaller = new MockOsgiInstaller(); - installer = MiscUtil.getJcrInstaller(repo, osgiInstaller); - } - - @Override - protected void tearDown() throws Exception { - super.tearDown(); - contentHelper.cleanupContent(); - session.logout(); - eventHelper = null; - contentHelper = null; - installer.deactivate(MiscUtil.getMockComponentContext()); - MiscUtil.waitForInstallerThread(installer, TIMEOUT); - } - - private void assertRegisteredPaths(String [] paths) { - for(String path : paths) { - assertRegistered(path, !path.contains("NOT")); - } - } - - private void assertRegistered(String path, boolean registered) { - if(registered) { - assertTrue("Expected " + path + " to be registered", - osgiInstaller.isRegistered(JcrInstaller.URL_SCHEME, path)); - } else { - assertFalse("Expected " + path + " to be unregistered", - osgiInstaller.isRegistered(JcrInstaller.URL_SCHEME, path)); - } - } - - private void assertRecordedCall(String action, String path) { - final String callStr = action + ":" + JcrInstaller.URL_SCHEME + ":" + path; - boolean found = false; - for(String call : osgiInstaller.getRecordedCalls()) { - if(call.startsWith(callStr)) { - found = true; - break; - } - } - assertTrue("Expecting '" + callStr + "' in recorded calls (" + osgiInstaller.getRecordedCalls() + ")", found); - } - public void testInitialResourceDetection() throws Exception { assertRegisteredPaths(contentHelper.FAKE_RESOURCES); assertRegisteredPaths(contentHelper.FAKE_CONFIGS); } - + + protected boolean needsTestContent() { + return true; + } + public void testAddFiles() throws Exception { final String [] paths = { "/libs/foo/bar/install/" + System.currentTimeMillis() + ".jar", Modified: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java?rev=810055&r1=810054&r2=810055&view=diff ============================================================================== --- sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java (original) +++ sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java Tue Sep 1 13:50:30 2009 @@ -79,7 +79,7 @@ @Test public void testIdleState() throws Exception { - Thread.sleep(JcrInstaller.RUN_LOOP_DELAY_MSEC * 2); + Thread.sleep(JcrInstaller.RUN_LOOP_DELAY_MSEC * 4); assertIdle(); }