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


Reply via email to