Author: bdelacretaz
Date: Fri Sep 11 09:43:50 2009
New Revision: 813746

URL: http://svn.apache.org/viewvc?rev=813746&view=rev
Log:
SLING-1078 - improved handling of JcrInstaller background thread, and fix tests 
that failed due to changes in go idle behavior

Modified:
    
sling/trunk/installer/jcr/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/impl/JcrInstaller.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=813746&r1=813745&r2=813746&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
 Fri Sep 11 09:43:50 2009
@@ -59,7 +59,7 @@
  *      name="service.vendor"
  *      value="The Apache Software Foundation"
  */
-public class JcrInstaller implements Runnable, EventListener {
+public class JcrInstaller implements EventListener {
        public static final long RUN_LOOP_DELAY_MSEC = 500L;
        public static final String URL_SCHEME = "jcrinstall";
 
@@ -123,9 +123,6 @@
     /** Session shared by all WatchedFolder */
     private Session session;
     
-    /** Used to stop background thread when deactivated */
-    private int deactivationCounter = 1;
-    
     /** The root folders that we watch */
     private String [] roots;
     
@@ -145,6 +142,29 @@
     
     /** Timer used to call updateFoldersList() */
     private final RescanTimer updateFoldersListTimer = new RescanTimer();
+    
+    /** Thread that can be cleanly stopped with a flag */
+    static int bgThreadCounter;
+    class StoppableThread extends Thread {
+        boolean active = true;
+        StoppableThread() {
+            synchronized (JcrInstaller.class) {
+                setName("JcrInstaller." + (++bgThreadCounter));
+            }
+            setDaemon(true);
+        }
+        
+        @Override
+        public final void run() {
+            log.info("Background thread {} starting", 
Thread.currentThread().getName());
+            while(active) {
+                runOneCycle();
+            }
+            log.info("Background thread {} done", 
Thread.currentThread().getName());
+            counters[RUN_LOOP_COUNTER] = -1;
+        }
+    };
+    private StoppableThread backgroundThread;
 
     protected void activate(ComponentContext context) throws Exception {
        
@@ -229,16 +249,27 @@
        log.info("Registering {} resources with OSGi installer: {}", 
resources.size(), resources);
        installer.registerResources(resources, URL_SCHEME);
        
-       final Thread t = new Thread(this, getClass().getSimpleName() + "." + 
deactivationCounter);
-       t.setDaemon(true);
-       t.start();
+       if(backgroundThread != null) {
+           throw new IllegalStateException("Expected backgroundThread to be 
null in activate()");
+       }
+        backgroundThread = new StoppableThread();
+        backgroundThread.start();
     }
     
     protected void deactivate(ComponentContext context) {
        log.info("deactivate()");
 
+       final long timeout = 30000L;
+       try {
+            backgroundThread.active = false;
+            log.debug("Waiting for " + backgroundThread.getName() + " Thread 
to end...");
+            backgroundThread.join(timeout);
+            backgroundThread = null;
+       } catch(InterruptedException iex) {
+           throw new IllegalStateException("backgroundThread.join interrupted 
after " + timeout + " msec");
+       }
+        
         try {
-            deactivationCounter++;
             folderNameFilter = null;
             watchedFolders = null;
             converters.clear();
@@ -415,60 +446,54 @@
     }
 
     /** Run periodic scans of our watched folders, and watch for folders 
creations/deletions */
-    public void run() {
-        log.info("Background thread {} starting", 
Thread.currentThread().getName());
-        final int savedCounter = deactivationCounter;
-        while(savedCounter == deactivationCounter) {
-            try {
-                // Rescan WatchedFolders if needed
-                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);
-                            installer.removeResource(r);
-                        }
-                        for(InstallableResource r : sr.toAdd) {
-                            log.info("Registering resource with OSGi 
installer: {}",r);
-                            installer.addResource(r);
-                        }
+    public void runOneCycle() {
+        try {
+            // Rescan WatchedFolders if needed
+            final boolean scanWf = WatchedFolder.getRescanTimer().expired(); 
+            if(scanWf) {
+                for(WatchedFolder wf : watchedFolders) {
+                    if(!wf.needsScan()) {
+                        continue;
                     }
-                }
-
-                // 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);
+                    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);
                         installer.removeResource(r);
                     }
+                    for(InstallableResource r : sr.toAdd) {
+                        log.info("Registering resource with OSGi installer: 
{}",r);
+                        installer.addResource(r);
+                    }
                 }
+            }
 
-                try {
-                    Thread.sleep(RUN_LOOP_DELAY_MSEC);
-                } catch(InterruptedException ignore) {
-                }
-                
-            } catch(Exception e) {
-                log.warn("Exception in run()", e);
-                try {
-                    Thread.sleep(RUN_LOOP_DELAY_MSEC);
-                } catch(InterruptedException ignore) {
+            // 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);
                 }
             }
-            counters[RUN_LOOP_COUNTER]++;
+
+            try {
+                Thread.sleep(RUN_LOOP_DELAY_MSEC);
+            } catch(InterruptedException ignore) {
+            }
+            
+        } catch(Exception e) {
+            log.warn("Exception in run()", e);
+            try {
+                Thread.sleep(RUN_LOOP_DELAY_MSEC);
+            } catch(InterruptedException ignore) {
+            }
         }
-        log.info("Background thread {} done", 
Thread.currentThread().getName());
-        counters[RUN_LOOP_COUNTER] = -1;
+        counters[RUN_LOOP_COUNTER]++;
     }
     
     long [] getCounters() {

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=813746&r1=813745&r2=813746&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
 Fri Sep 11 09:43:50 2009
@@ -123,15 +123,16 @@
         return cc;
     }
     
-    static private void waitForCycles(JcrInstaller installer, int nCycles, 
long timeoutMsec) throws Exception {
-        final long endCycles = 
installer.getCounters()[JcrInstaller.RUN_LOOP_COUNTER];
+    static private void waitForCycles(JcrInstaller installer, long 
initialCycleCount, int expectedCycles, long timeoutMsec) throws Exception {
         final long endTime = System.currentTimeMillis() + timeoutMsec;
+        long cycles = 0;
         while(System.currentTimeMillis() < endTime) {
-            if(installer.getCounters()[JcrInstaller.RUN_LOOP_COUNTER] > 
endCycles) {
+            cycles = installer.getCounters()[JcrInstaller.RUN_LOOP_COUNTER] - 
initialCycleCount;
+            if(cycles >= expectedCycles) {
                 return;
             }
         }
-        throw new Exception("did not get " + nCycles + " installer cycles in " 
+ timeoutMsec + " msec"); 
+        throw new Exception("did not get " + expectedCycles + " installer 
cycles in " + timeoutMsec + " msec, got " + cycles); 
     }
     
     /** Get the WatchedFolders of supplied JcrInstaller */
@@ -144,12 +145,14 @@
     
     /** Wait long enough for all changes in content to be processed by 
JcrInstaller */ 
     static void waitAfterContentChanges(EventHelper eventHelper, JcrInstaller 
installer) throws Exception {
+        final long startCycles = 
installer.getCounters()[JcrInstaller.RUN_LOOP_COUNTER];
+        
         // 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);
+        // And wait for one JcrInstaller run cycle
+        MiscUtil.waitForCycles(installer, startCycles, 1, 5000L);
     }
     
     /** Wait until installer thread exits */

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=813746&r1=813745&r2=813746&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
 Fri Sep 11 09:43:50 2009
@@ -190,20 +190,25 @@
        assertRegistered(path, true);
        
        // Setup a known value for the config
+        osgiInstaller.clearRecordedCalls();
        final String key = "foo" + System.currentTimeMillis();
        final String value = "value" + System.currentTimeMillis();
        ((Node)session.getItem(path)).setProperty(key, value);
        session.save();
         MiscUtil.waitAfterContentChanges(eventHelper, installer);
+        assertEquals("Expected one OsgiInstaller call for initial config 
change",
+                1, osgiInstaller.getRecordedCalls().size());
                        
        // Make a change that does not influence the configs's digest,
        // and verify that no OSGi registrations result
-       int nCalls = osgiInstaller.getRecordedCalls().size();
+        osgiInstaller.clearRecordedCalls();
        ((Node)session.getItem(path)).setProperty(key, value);
        session.save();
         MiscUtil.waitAfterContentChanges(eventHelper, installer);
-        assertEquals("Expected no OsgiInstaller calls for no-impact config 
change",
-                       nCalls, osgiInstaller.getRecordedCalls().size());
+        assertEquals(
+                "Expected no OsgiInstaller calls for no-impact config change, 
got " 
+                + osgiInstaller.getRecordedCalls(),
+                       0, osgiInstaller.getRecordedCalls().size());
         
         // Make a content change -> resource must be re-registered
         osgiInstaller.clearRecordedCalls();


Reply via email to