Author: bdelacretaz Date: Thu Aug 27 15:19:08 2009 New Revision: 808453 URL: http://svn.apache.org/viewvc?rev=808453&view=rev Log: SLING-1078 - avoid unnecessary scans in JcrInstaller.run() loop
Added: sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/RescanTimer.java (with props) sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/RootFolderListener.java - copied, changed from r808152, sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolderCreationListener.java sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java (with props) Removed: sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolderCreationListener.java 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/ContentHelper.java sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FindPathsToWatchTest.java sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/MiscUtil.java sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ResourceDetectionTest.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=808453&r1=808452&r2=808453&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 Thu Aug 27 15:19:08 2009 @@ -61,6 +61,13 @@ public static final String URL_SCHEME = "jcrinstall"; private final Logger log = LoggerFactory.getLogger(getClass()); + + /** Counters, used for statistics and testing */ + private final long [] counters = new long[COUNTERS_COUNT]; + public static final int SCAN_FOLDERS_COUNTER = 0; + public static final int UPDATE_FOLDERS_LIST_COUNTER = 1; + public static final int RUN_LOOP_COUNTER = 2; + public static final int COUNTERS_COUNT = 3; /** This class watches the repository for installable resources * @scr.reference @@ -110,9 +117,6 @@ /** Session shared by all WatchedFolder */ private Session session; - /** Count cycles of our run() method, used in testing */ - private int cyclesCount; - /** Used to stop background thread when deactivated */ private int deactivationCounter = 1; @@ -125,8 +129,11 @@ private final Collection <NodeConverter> converters = new ArrayList<NodeConverter>(); /** Detect newly created folders that we must watch */ - private final List<WatchedFolderCreationListener> listeners = new LinkedList<WatchedFolderCreationListener>(); + private final List<RootFolderListener> listeners = new LinkedList<RootFolderListener>(); + /** Timer used to call updateFoldersList() */ + private final RescanTimer updateFoldersListTimer = new RescanTimer(); + protected void activate(ComponentContext context) throws Exception { log.info("activate()"); @@ -170,7 +177,7 @@ // Setup folder filtering and watching folderNameFilter = new FolderNameFilter(roots, folderNameRegexp, runMode); for (String path : roots) { - listeners.add(new WatchedFolderCreationListener(session, folderNameFilter, path)); + listeners.add(new RootFolderListener(session, folderNameFilter, path, updateFoldersListTimer)); } // Find paths to watch and create WatchedFolders to manage them @@ -204,7 +211,7 @@ watchedFolders = null; converters.clear(); if(session != null) { - for(WatchedFolderCreationListener wfc : listeners) { + for(RootFolderListener wfc : listeners) { wfc.cleanup(session); } session.logout(); @@ -259,7 +266,7 @@ } final int depth = path.split("/").length; if(depth > maxWatchedFolderDepth) { - log.debug("Not recursing into {} due to maxWatchedFolderDepth={}", path, maxWatchedFolderDepth); + log.info("Not recursing into {} due to maxWatchedFolderDepth={}", path, maxWatchedFolderDepth); return; } else { final NodeIterator it = n.getNodes(); @@ -283,15 +290,27 @@ * @return a list of InstallableResource that must be unregistered, * for folders that have been removed */ - private List<InstallableResource> addAndDeleteFolders() throws Exception { + private List<InstallableResource> updateFoldersList() throws Exception { final List<InstallableResource> result = new LinkedList<InstallableResource>(); - for(WatchedFolderCreationListener wfc : listeners) { - final Set<String> newPaths = wfc.getAndClearPaths(); - if(newPaths != null && newPaths.size() > 0) { - log.info("Detected {} new folder(s) to watch", newPaths.size()); - for(String path : newPaths) { - watchedFolders.add( - new WatchedFolder(session, path, folderNameFilter.getPriority(path), URL_SCHEME, converters)); + for(RootFolderListener wfc : listeners) { + final Set<String> changedPaths = wfc.getAndClearPaths(); + if(changedPaths != null && changedPaths.size() > 0) { + log.debug("Detected {} paths with possible watched folder changes", changedPaths.size()); + 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)); + } + } } } } @@ -299,13 +318,13 @@ final List<WatchedFolder> toRemove = new ArrayList<WatchedFolder>(); for(WatchedFolder wf : watchedFolders) { if(!session.itemExists(wf.getPath())) { - log.info("Deleting {}, path does not exist anymore", wf); result.addAll(wf.scan().toRemove); wf.cleanup(); toRemove.add(wf); } } for(WatchedFolder wf : toRemove) { + log.info("Deleting {}, path does not exist anymore", wf); watchedFolders.remove(wf); } @@ -318,15 +337,15 @@ final int savedCounter = deactivationCounter; while(savedCounter == deactivationCounter) { try { - final List<InstallableResource> toRemove = addAndDeleteFolders(); - for(InstallableResource r : toRemove) { - log.info("Removing resource from OSGi installer (folder deleted): {}",r); - installer.removeResource(r); - } - // Rescan WatchedFolders if needed - if(System.currentTimeMillis() > WatchedFolder.getNextScanTime()) { + final boolean scanWf = WatchedFolder.getRescanTimer().expired(); + if(scanWf) { for(WatchedFolder wf : watchedFolders) { + if(!wf.needsScan()) { + continue; + } + WatchedFolder.getRescanTimer().reset(); + counters[SCAN_FOLDERS_COUNTER]++; final WatchedFolder.ScanResult sr = wf.scan(); for(InstallableResource r : sr.toRemove) { log.info("Removing resource from OSGi installer: {}",r); @@ -338,9 +357,19 @@ } } } - cyclesCount++; - // TODO wait for events from our listeners, and/or WatchedFolder scan time + // Update list of WatchedFolder if we got any relevant events, + // or if there were any WatchedFolder events + if(scanWf || updateFoldersListTimer.expired()) { + updateFoldersListTimer.reset(); + counters[UPDATE_FOLDERS_LIST_COUNTER]++; + final List<InstallableResource> toRemove = updateFoldersList(); + for(InstallableResource r : toRemove) { + log.info("Removing resource from OSGi installer (folder deleted): {}",r); + installer.removeResource(r); + } + } + try { Thread.sleep(RUN_LOOP_DELAY_MSEC); } catch(InterruptedException ignore) { @@ -353,8 +382,13 @@ } catch(InterruptedException ignore) { } } + counters[RUN_LOOP_COUNTER]++; } - log.info("Background thread {} stopping", Thread.currentThread().getName()); + log.info("Background thread {} done", Thread.currentThread().getName()); + counters[RUN_LOOP_COUNTER] = -1; + } + + long [] getCounters() { + return counters; } - } \ No newline at end of file Added: sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/RescanTimer.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/RescanTimer.java?rev=808453&view=auto ============================================================================== --- sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/RescanTimer.java (added) +++ sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/RescanTimer.java Thu Aug 27 15:19:08 2009 @@ -0,0 +1,41 @@ +/* + * 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; + +/** Used to wait at least SCAN_DELAY_MSEC before rescanning + * things after receiving a JCR Event. This avoids useless + * scanning, as events usually come in bursts. + */ + +class RescanTimer { + public static final long SCAN_DELAY_MSEC = 500L; + private long nextScanTime = Long.MAX_VALUE; + + synchronized void scheduleScan() { + nextScanTime = System.currentTimeMillis() + SCAN_DELAY_MSEC; + } + + synchronized void reset() { + nextScanTime = Long.MAX_VALUE; + } + + boolean expired() { + return System.currentTimeMillis() > nextScanTime; + } +} Propchange: sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/RescanTimer.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/RescanTimer.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev URL Copied: sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/RootFolderListener.java (from r808152, sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolderCreationListener.java) URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/RootFolderListener.java?p2=sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/RootFolderListener.java&p1=sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolderCreationListener.java&r1=808152&r2=808453&rev=808453&view=diff ============================================================================== --- sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/WatchedFolderCreationListener.java (original) +++ sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/RootFolderListener.java Thu Aug 27 15:19:08 2009 @@ -30,24 +30,34 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** Listen for JCR events to find out when new WatchedFolders - * must be created. +/** Listen for JCR events under one of our roots, to find out + * when new WatchedFolders must be created, or when some might + * have been deleted. */ -class WatchedFolderCreationListener implements EventListener { +class RootFolderListener implements EventListener { protected final Logger log = LoggerFactory.getLogger(this.getClass()); private Set<String> paths = new HashSet<String>(); private final FolderNameFilter folderNameFilter; + private final RescanTimer timer; + private final String watchedPath; - WatchedFolderCreationListener(Session session, FolderNameFilter fnf, String path) throws RepositoryException { + RootFolderListener(Session session, FolderNameFilter fnf, String path, RescanTimer timer) throws RepositoryException { folderNameFilter = fnf; + this.timer = timer; + this.watchedPath = path; - int eventTypes = Event.NODE_ADDED; + int eventTypes = Event.NODE_ADDED | Event.NODE_REMOVED; boolean isDeep = true; boolean noLocal = true; session.getWorkspace().getObservationManager().addEventListener(this, eventTypes, path, isDeep, null, null, noLocal); } + @Override + public String toString() { + return getClass().getSimpleName() + " (" + watchedPath + ")"; + } + void cleanup(Session session) throws RepositoryException { session.getWorkspace().getObservationManager().removeEventListener(this); } @@ -72,7 +82,10 @@ try { while(it.hasNext()) { final Event e = it.nextEvent(); - if(folderNameFilter.getPriority(e.getPath()) > 0) { + // Rescan on all NODE_REMOVED events, to be on the safe side: + // an install folder might have been removed, and (I think) this is + // the safest way of finding out. + if(e.getType() == Event.NODE_REMOVED || folderNameFilter.getPriority(e.getPath()) > 0) { synchronized(paths) { paths.add(e.getPath()); } @@ -81,5 +94,7 @@ } catch(RepositoryException re) { log.warn("RepositoryException in onEvent", re); } + log.debug("{} got JCR events, scheduling rescan of {}", this, paths); + timer.scheduleScan(); } } 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=808453&r1=808452&r2=808453&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 Thu Aug 27 15:19:08 2009 @@ -46,7 +46,7 @@ private final String path; private final int priority; private final Session session; - private static long nextScanTime; + private static RescanTimer rescanTimer = new RescanTimer(); private boolean needsScan; private final String urlScheme; private final Collection <JcrInstaller.NodeConverter> converters; @@ -61,12 +61,12 @@ /** Store the digests of the last returned resources, keyed by path, to detect changes */ private final Map<String, String> digests = new HashMap<String, String>(); - // WatchedFolders that need a rescan will be scanned - // once no JCR events have been received for this amount of time. - public static final long SCAN_DELAY_MSEC = 500L; - WatchedFolder(Session session, String path, int priority, String urlScheme, Collection<JcrInstaller.NodeConverter> converters) throws RepositoryException { + if(priority < 1) { + throw new IllegalArgumentException("Cannot watch folder with priority 0:" + path); + } + this.path = path; this.converters = converters; this.priority = priority; @@ -105,7 +105,7 @@ /** Set a static "timer" whenever an event occurs */ public void onEvent(EventIterator it) { - nextScanTime = System.currentTimeMillis() + SCAN_DELAY_MSEC; + rescanTimer.scheduleScan(); needsScan = true; log.debug("Event received, scheduling scan of {}", path); } @@ -114,13 +114,14 @@ return needsScan; } - static long getNextScanTime() { - return nextScanTime; + static RescanTimer getRescanTimer() { + return rescanTimer; } /** Scan the contents of our folder and return the corresponding InstallableResource */ ScanResult scan() throws Exception { log.debug("Scanning {}", path); + needsScan = false; Node folder = null; if(session.itemExists(path)) { Modified: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ContentHelper.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ContentHelper.java?rev=808453&r1=808452&r2=808453&view=diff ============================================================================== --- sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ContentHelper.java (original) +++ sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ContentHelper.java Thu Aug 27 15:19:08 2009 @@ -94,12 +94,7 @@ void setupContent() throws Exception { cleanupContent(); - for(String folder : WATCHED_FOLDERS) { - createFolder(folder); - } - for(String folder : IGNORED_FOLDERS) { - createFolder(folder); - } + setupFolders(); for(String path : FAKE_RESOURCES) { createOrUpdateFile(path); } @@ -108,6 +103,15 @@ } } + void setupFolders() throws Exception { + for(String folder : WATCHED_FOLDERS) { + createFolder(folder); + } + for(String folder : IGNORED_FOLDERS) { + createFolder(folder); + } + } + void createFolder(String path) throws Exception { final String [] parts = relPath(path).split("/"); Node n = session.getRootNode(); Modified: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FindPathsToWatchTest.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FindPathsToWatchTest.java?rev=808453&r1=808452&r2=808453&view=diff ============================================================================== --- sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FindPathsToWatchTest.java (original) +++ sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/FindPathsToWatchTest.java Thu Aug 27 15:19:08 2009 @@ -30,6 +30,8 @@ */ public class FindPathsToWatchTest extends RepositoryTestBase { + public static final long TIMEOUT = 5000L; + SlingRepository repo; Session session; private EventHelper eventHelper; @@ -57,6 +59,8 @@ session.logout(); eventHelper = null; contentHelper = null; + installer.deactivate(MiscUtil.getMockComponentContext()); + MiscUtil.waitForInstallerThread(installer, TIMEOUT); } private boolean isWatched(String path, Collection<WatchedFolder> wfList) { @@ -67,7 +71,7 @@ } return false; } - + public void testInitialFind() throws Exception { final Collection<WatchedFolder> wf = MiscUtil.getWatchedFolders(installer); assertEquals("activate() must find all watched folders", contentHelper.WATCHED_FOLDERS.length, wf.size()); @@ -94,8 +98,7 @@ // Create folder, wait for observation event and check that // it is detected contentHelper.createFolder(newPath); - eventHelper.waitForEvents(5000L); - MiscUtil.waitForCycles(installer, 2, 5000L); + MiscUtil.waitAfterContentChanges(eventHelper, installer); if(newPath.contains("NOT")) { assertFalse(newPath + " must not be watched after test", isWatched(newPath, @@ -111,10 +114,10 @@ final int nBefore = MiscUtil.getWatchedFolders(installer).size(); contentHelper.delete(folder); eventHelper.waitForEvents(5000L); - MiscUtil.waitForCycles(installer, 2, 5000L); + MiscUtil.waitAfterContentChanges(eventHelper, installer); assertFalse(folder + " must not be watched anymore after deleting", isWatched(folder, MiscUtil.getWatchedFolders(installer))); - assertEquals("Expecting only one WatchedFolder to be deleted", nBefore - 1, + assertEquals("Expecting only one WatchedFolder to be deleted", nBefore - 1, MiscUtil.getWatchedFolders(installer).size()); } @@ -122,8 +125,7 @@ final Collection<WatchedFolder> wf = MiscUtil.getWatchedFolders(installer); assertEquals("activate() must find all watched folders", contentHelper.WATCHED_FOLDERS.length, wf.size()); contentHelper.cleanupContent(); - eventHelper.waitForEvents(5000L); - MiscUtil.waitForCycles(installer, 2, 5000L); + MiscUtil.waitAfterContentChanges(eventHelper, installer); assertEquals("After deleting content, no more folders must be watched", 0, wf.size()); } } \ No newline at end of file Modified: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/MiscUtil.java URL: http://svn.apache.org/viewvc/sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/MiscUtil.java?rev=808453&r1=808452&r2=808453&view=diff ============================================================================== --- sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/MiscUtil.java (original) +++ sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/MiscUtil.java Thu Aug 27 15:19:08 2009 @@ -133,13 +133,11 @@ return cc; } - static void waitForCycles(JcrInstaller installer, int nCycles, long timeoutMsec) throws Exception { - final Field f = installer.getClass().getDeclaredField("cyclesCount"); - f.setAccessible(true); - final int endCycles = ((Integer)f.get(installer)).intValue() + nCycles; + static private void waitForCycles(JcrInstaller installer, int nCycles, long timeoutMsec) throws Exception { + final long endCycles = installer.getCounters()[JcrInstaller.RUN_LOOP_COUNTER]; final long endTime = System.currentTimeMillis() + timeoutMsec; while(System.currentTimeMillis() < endTime) { - if(((Integer)f.get(installer)).intValue() > endCycles) { + if(installer.getCounters()[JcrInstaller.RUN_LOOP_COUNTER] > endCycles) { return; } } @@ -153,4 +151,25 @@ f.setAccessible(true); return (Collection<WatchedFolder>)f.get(installer); } + + /** Wait long enough for all changes in content to be processed by JcrInstaller */ + static void waitAfterContentChanges(EventHelper eventHelper, JcrInstaller installer) throws Exception { + // First wait for all JCR events to be delivered + eventHelper.waitForEvents(5000L); + // RescanTimer causes a SCAN_DELAY_MSEC wait after JCR events are received + Thread.sleep(RescanTimer.SCAN_DELAY_MSEC * 2); + // And wait for 2 complete JcrInstaller run cycles, to be on the safe side + MiscUtil.waitForCycles(installer, 3, 5000L); + } + + /** Wait until installer thread exits */ + static void waitForInstallerThread(JcrInstaller installer, long timeoutMsec) throws Exception { + final long endTime = System.currentTimeMillis() + timeoutMsec; + while(System.currentTimeMillis() < endTime) { + if(installer.getCounters()[JcrInstaller.RUN_LOOP_COUNTER] == -1) { + return; + } + } + throw new Exception("JcrInstaller thread did not signal its end (timeout=" + timeoutMsec + " msec)"); + } } 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=808453&r1=808452&r2=808453&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 Thu Aug 27 15:19:08 2009 @@ -58,6 +58,8 @@ session.logout(); eventHelper = null; contentHelper = null; + installer.deactivate(MiscUtil.getMockComponentContext()); + MiscUtil.waitForInstallerThread(installer, TIMEOUT); } private void assertRegisteredPaths(String [] paths) { @@ -106,8 +108,7 @@ for(String path : paths) { contentHelper.createOrUpdateFile(path); } - eventHelper.waitForEvents(TIMEOUT); - MiscUtil.waitForCycles(installer, 2, TIMEOUT); + MiscUtil.waitAfterContentChanges(eventHelper, installer); assertRegisteredPaths(paths); } @@ -125,8 +126,7 @@ contentHelper.createConfig(path, null); } - eventHelper.waitForEvents(TIMEOUT); - MiscUtil.waitForCycles(installer, 2, TIMEOUT); + MiscUtil.waitAfterContentChanges(eventHelper, installer); assertRegisteredPaths(paths); } @@ -140,8 +140,7 @@ final int toRemove = 1; contentHelper.delete(contentHelper.FAKE_RESOURCES[toRemove]); contentHelper.delete(contentHelper.FAKE_CONFIGS[toRemove]); - eventHelper.waitForEvents(TIMEOUT); - MiscUtil.waitForCycles(installer, 2, TIMEOUT); + MiscUtil.waitAfterContentChanges(eventHelper, installer); for(int i=0; i < contentHelper.FAKE_RESOURCES.length; i++) { assertRegistered(contentHelper.FAKE_RESOURCES[i], i != toRemove); @@ -184,7 +183,7 @@ // Restart JcrInstaller and verify that all remaining resources are re-registered installer.activate(cc); - MiscUtil.waitForCycles(installer, 2, TIMEOUT); + MiscUtil.waitAfterContentChanges(eventHelper, installer); for(int i=0; i < contentHelper.FAKE_RESOURCES.length; i++) { final String path = contentHelper.FAKE_RESOURCES[i]; @@ -213,8 +212,7 @@ // Removing a folder, all resources that it contains must be unregistered contentHelper.delete("/libs"); - eventHelper.waitForEvents(TIMEOUT); - MiscUtil.waitForCycles(installer, 2, TIMEOUT); + MiscUtil.waitAfterContentChanges(eventHelper, installer); for(String path : contentHelper.FAKE_RESOURCES) { assertRegistered(path, !path.startsWith("/libs")); } @@ -232,16 +230,14 @@ int nCalls = osgiInstaller.getRecordedCalls().size(); ((Node)session.getItem(path + "/jcr:content")).setProperty("jcr:mimeType", "application/" + getClass().getName()); session.save(); - eventHelper.waitForEvents(TIMEOUT); - MiscUtil.waitForCycles(installer, 2, TIMEOUT); + MiscUtil.waitAfterContentChanges(eventHelper, installer); assertEquals("Expected no OsgiInstaller calls for no-impact file change", nCalls, osgiInstaller.getRecordedCalls().size()); // Make a content change -> resource must be re-registered osgiInstaller.clearRecordedCalls(); contentHelper.createOrUpdateFile(path, null, System.currentTimeMillis()); - eventHelper.waitForEvents(TIMEOUT); - MiscUtil.waitForCycles(installer, 2, TIMEOUT); + MiscUtil.waitAfterContentChanges(eventHelper, installer); assertEquals("Expected one OsgiInstaller call for file content change", 1, osgiInstaller.getRecordedCalls().size()); assertRecordedCall("add", path); @@ -256,24 +252,21 @@ final String value = "value" + System.currentTimeMillis(); ((Node)session.getItem(path)).setProperty(key, value); session.save(); - eventHelper.waitForEvents(TIMEOUT); - MiscUtil.waitForCycles(installer, 2, TIMEOUT); + MiscUtil.waitAfterContentChanges(eventHelper, installer); // Make a change that does not influence the configs's digest, // and verify that no OSGi registrations result int nCalls = osgiInstaller.getRecordedCalls().size(); ((Node)session.getItem(path)).setProperty(key, value); session.save(); - eventHelper.waitForEvents(TIMEOUT); - MiscUtil.waitForCycles(installer, 2, TIMEOUT); + MiscUtil.waitAfterContentChanges(eventHelper, installer); assertEquals("Expected no OsgiInstaller calls for no-impact config change", nCalls, osgiInstaller.getRecordedCalls().size()); // Make a content change -> resource must be re-registered osgiInstaller.clearRecordedCalls(); ((Node)session.getItem(path)).setProperty(key, value + "-changed"); - eventHelper.waitForEvents(TIMEOUT); - MiscUtil.waitForCycles(installer, 2, TIMEOUT); + MiscUtil.waitAfterContentChanges(eventHelper, installer); assertEquals("Expected one OsgiInstaller call for config content change", 1, osgiInstaller.getRecordedCalls().size()); assertRecordedCall("add", path); Added: 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=808453&view=auto ============================================================================== --- sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java (added) +++ sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java Thu Aug 27 15:19:08 2009 @@ -0,0 +1,138 @@ +/* + * 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; +import org.junit.Test; + +/** Verify that JcrInstaller scans folders only when needed */ +public class ScanningLoopTest extends RepositoryTestBase { + public static final long TIMEOUT = 5000L; + + private JcrInstaller installer; + private SlingRepository repository; + private MockOsgiInstaller osgiInstaller; + private ContentHelper contentHelper; + private Session session; + private EventHelper eventHelper; + + @Override + public void setUp() throws Exception { + super.setUp(); + repository = getRepository(); + osgiInstaller = new MockOsgiInstaller(); + installer = MiscUtil.getJcrInstaller(repository, osgiInstaller); + session = repository.loginAdministrative(repository.getDefaultWorkspace()); + eventHelper = new EventHelper(session); + contentHelper = new ContentHelper(session); + contentHelper.setupFolders(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + installer.deactivate(MiscUtil.getMockComponentContext()); + MiscUtil.waitForInstallerThread(installer, TIMEOUT); + installer = null; + contentHelper.cleanupContent(); + if(session != null) { + session.logout(); + session = null; + } + } + + private void assertCounter(int index, long value) { + assertEquals("Counter " + index, value, installer.getCounters()[index]); + } + + private void assertIdle() throws Exception { + final long sf = installer.getCounters()[JcrInstaller.SCAN_FOLDERS_COUNTER]; + final long uc = installer.getCounters()[JcrInstaller.UPDATE_FOLDERS_LIST_COUNTER]; + Thread.sleep(JcrInstaller.RUN_LOOP_DELAY_MSEC * 2); + assertCounter(JcrInstaller.SCAN_FOLDERS_COUNTER, sf); + assertCounter(JcrInstaller.UPDATE_FOLDERS_LIST_COUNTER, uc); + } + + private void assertEvents(String info, long oldCount, int counterIndex) { + final long newCount = installer.getCounters()[counterIndex]; + assertTrue(info + " (old=" + oldCount + ", new=" + newCount + ")", newCount > oldCount); + } + + @Test + public void testIdleState() throws Exception { + Thread.sleep(JcrInstaller.RUN_LOOP_DELAY_MSEC * 2); + assertIdle(); + } + + @Test + public void testAddBundle() throws Exception { + contentHelper.createOrUpdateFile(contentHelper.FAKE_RESOURCES[0]); + eventHelper.waitForEvents(TIMEOUT); + Thread.sleep(JcrInstaller.RUN_LOOP_DELAY_MSEC * 2); + assertIdle(); + } + + public void testAddContentOutside() throws Exception { + final long sf = installer.getCounters()[JcrInstaller.SCAN_FOLDERS_COUNTER]; + final long uc = installer.getCounters()[JcrInstaller.UPDATE_FOLDERS_LIST_COUNTER]; + + contentHelper.createOrUpdateFile("/" + System.currentTimeMillis()); + eventHelper.waitForEvents(TIMEOUT); + Thread.sleep(JcrInstaller.RUN_LOOP_DELAY_MSEC * 2); + + // Adding a file outside /libs or /apps must not "wake up" the scan loop + assertCounter(JcrInstaller.SCAN_FOLDERS_COUNTER, sf); + assertCounter(JcrInstaller.UPDATE_FOLDERS_LIST_COUNTER, uc); + } + + public void testDeleteFile() throws Exception { + contentHelper.setupContent(); + eventHelper.waitForEvents(TIMEOUT); + Thread.sleep(JcrInstaller.RUN_LOOP_DELAY_MSEC * 2); + assertIdle(); + + final long sf = installer.getCounters()[JcrInstaller.SCAN_FOLDERS_COUNTER]; + contentHelper.delete(contentHelper.FAKE_RESOURCES[0]); + eventHelper.waitForEvents(TIMEOUT); + Thread.sleep(JcrInstaller.RUN_LOOP_DELAY_MSEC * 2); + assertEvents("Expected at least one folder scan event", + sf, JcrInstaller.SCAN_FOLDERS_COUNTER); + + assertIdle(); + } + + public void testDeleteLibsFolder() throws Exception { + contentHelper.setupContent(); + eventHelper.waitForEvents(TIMEOUT); + Thread.sleep(JcrInstaller.RUN_LOOP_DELAY_MSEC * 2); + assertIdle(); + + final long uc = installer.getCounters()[JcrInstaller.UPDATE_FOLDERS_LIST_COUNTER]; + contentHelper.delete("/libs"); + eventHelper.waitForEvents(TIMEOUT); + Thread.sleep(JcrInstaller.RUN_LOOP_DELAY_MSEC * 2); + assertEvents("Expected at least one folders list update event", + uc, JcrInstaller.UPDATE_FOLDERS_LIST_COUNTER); + + assertIdle(); + } +} \ No newline at end of file Propchange: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: sling/trunk/installer/jcr/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/impl/ScanningLoopTest.java ------------------------------------------------------------------------------ svn:keywords = Author Date Id Revision Rev URL