You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2015/07/09 13:05:59 UTC

svn commit: r1690063 - in /sling/whiteboard/bdelacretaz/startup-stuff: it-startup/src/test/java/org/apache/sling/launchpad/it/startup/ startup-sequencer/src/main/java/org/apache/sling/startup/sequencer/ startup-sequencer/src/main/java/org/apache/sling/...

Author: bdelacretaz
Date: Thu Jul  9 11:05:58 2015
New Revision: 1690063

URL: http://svn.apache.org/r1690063
Log:
SLING-4851 - it works! Each test bundle's SlowActivator now finishes within the same start level where the bundle was asynchronously installed via the OSGi installer

Modified:
    sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/AsyncInstaller.java
    sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/IncrementalStartupIT.java
    sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/P.java
    sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/SlowActivator.java
    sling/whiteboard/bdelacretaz/startup-stuff/startup-sequencer/src/main/java/org/apache/sling/startup/sequencer/StartLevelChangeBarrier.java
    sling/whiteboard/bdelacretaz/startup-stuff/startup-sequencer/src/main/java/org/apache/sling/startup/sequencer/impl/StartLevelControllerImpl.java

Modified: sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/AsyncInstaller.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/AsyncInstaller.java?rev=1690063&r1=1690062&r2=1690063&view=diff
==============================================================================
--- sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/AsyncInstaller.java (original)
+++ sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/AsyncInstaller.java Thu Jul  9 11:05:58 2015
@@ -24,6 +24,7 @@ import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Dictionary;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.List;
@@ -31,16 +32,21 @@ import java.util.Map;
 import java.util.Random;
 import java.util.Set;
 import java.util.UUID;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.apache.sling.installer.api.InstallableResource;
 import org.apache.sling.installer.api.OsgiInstaller;
+import org.apache.sling.startup.sequencer.StartLevelChangeBarrier;
 import org.ops4j.pax.tinybundles.core.TinyBundles;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.FrameworkEvent;
 import org.osgi.framework.FrameworkListener;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.startlevel.StartLevel;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -48,7 +54,7 @@ import org.slf4j.LoggerFactory;
 /** Asynchronously installs test bundles when start levels change,
  *  to verify the Sling incremental startup mechanism. 
  */
-class AsyncInstaller implements FrameworkListener {
+class AsyncInstaller implements FrameworkListener, StartLevelChangeBarrier {
     
     private final Logger log = LoggerFactory.getLogger(getClass());
     private final BundleContext bundleContext;
@@ -60,6 +66,13 @@ class AsyncInstaller implements Framewor
     private final Random random = new Random(42);
     private final AtomicInteger counter = new AtomicInteger();
     private int lastStartLevel = -1;
+    private final List<ServiceRegistration> toCleanup = new ArrayList<ServiceRegistration>();
+    
+    /** Map of InstallableResource keyed by start level - the test bundles to install */
+    private final Map<Integer, List<InstallableResource>> resourceMap = new HashMap<Integer, List<InstallableResource>>();
+    
+    /** Map of CountDownLatch keyed by start level - the latches that our bundle activators decrement */
+    private final ConcurrentMap<Integer, CountDownLatch> latchMap = new ConcurrentHashMap<Integer, CountDownLatch>();
     
     /** TinyBundles.add(...) has trouble with classloaders in our environment, so
      *  add classes via the classpath resources mechanism. 
@@ -90,8 +103,30 @@ class AsyncInstaller implements Framewor
         startLevelService = s;
         installer = inst;
         bundleContext.addFrameworkListener(this);
+        toCleanup.add(bundleContext.registerService(StartLevelChangeBarrier.class.getName(), this, null));
+        toCleanup.add(bundleContext.registerService(ConcurrentMap.class.getName(), latchMap, null));
+    }
+    
+    void cleanup() {
+        for(ServiceRegistration reg : toCleanup) {
+            reg.unregister();
+        }
     }
     
+    @Override
+    public synchronized CountDownLatch getStartLevelLatch(int startLevel) {
+        // Simulate some resource discovery latency 
+        WaitFor.randomWait(156);
+        
+        // Create bundles for specified start level if needed,
+        // add their latch to the latch map and return it
+        final List<InstallableResource> res = getOrCreateInstallableResources(startLevel);
+        final CountDownLatch latch = new CountDownLatch(res.size());
+        latchMap.put(startLevel, latch);
+        log.info("Returning CountDownLatch({}) for start level {}", latch.getCount(), startLevel);
+        return latch;
+    }
+
     private InputStream getTestBundleStream(String bundleSymbolicName) throws Exception {
         final EmbeddedClass activator = new EmbeddedClass(SlowActivator.class);
         final EmbeddedClass waitFor = new EmbeddedClass(WaitFor.class);
@@ -103,26 +138,42 @@ class AsyncInstaller implements Framewor
             .build(TinyBundles.withBnd());
     }
     
-    void installMoreBundles() throws Exception {
-        final int n = (int)(random.nextFloat() * P.MAX_BUNDLES_PER_LEVEL);
-        final int startLevel = startLevelService.getStartLevel();
-        log.info("Installing {} test bundles at start level {}", n, startLevel);
-        
-        for(int i=0; i < n; i++) {
-            final String bsn = bundleNamePrefix + startLevel + "." + counter.incrementAndGet();
-            final InputStream is = getTestBundleStream(bsn);
-            final Dictionary<String, Object> dict = new Hashtable<String, Object>();
-            dict.put(InstallableResource.BUNDLE_START_LEVEL, startLevel);
-            resources.add(new InstallableResource(bsn, is, dict, bsn, "bundle", 100));
-            installedBundles.add(bsn);
+    synchronized void installBundles(int startLevel) throws Exception {
+        // Simulate some latency 
+        WaitFor.randomWait(93);
+        
+        final List<InstallableResource> res = getOrCreateInstallableResources(startLevel);
+        log.info("Installing {} InstallableResource (bundles) for start level {}", res.size(), startLevel);
+        for(InstallableResource r : res) {
+            installedBundles.add(r.getId());
         }
-        
-        // Simulate some resource discovery latency 
-        WaitFor.randomWait(156);
+        resources.addAll(res);
         installer.registerResources(getClass().getSimpleName(), resources.toArray(new InstallableResource[]{}));
         log.info("A total of {} InstallableResources are now registered", resources.size());
     }
     
+    synchronized List<InstallableResource> getOrCreateInstallableResources(int startLevel) {
+        List<InstallableResource> res = resourceMap.get(startLevel);
+        if(res == null) {
+            try {
+                final int n = (int)(random.nextFloat() * P.MAX_BUNDLES_PER_LEVEL) + 1;
+                log.info("Preparing {} InstallableResources for start level {}", n, startLevel);
+                res = new ArrayList<InstallableResource>();
+                resourceMap.put(startLevel, res);
+                for(int i=0; i < n; i++) {
+                    final String bsn = bundleNamePrefix + startLevel + "." + counter.incrementAndGet();
+                    final InputStream is = getTestBundleStream(bsn);
+                    final Dictionary<String, Object> dict = new Hashtable<String, Object>();
+                    dict.put(InstallableResource.BUNDLE_START_LEVEL, startLevel);
+                    res.add(new InstallableResource(bsn, is, dict, bsn, "bundle", 100));
+                }
+            } catch(Exception e) {
+                throw new RuntimeException("Exception creating test bundles", e);
+            }
+        }
+        return res;
+    }
+    
     boolean isTestBundle(Bundle b) {
         return b.getSymbolicName().startsWith(bundleNamePrefix);
     }
@@ -181,9 +232,7 @@ class AsyncInstaller implements Framewor
             final int level = startLevelService.getStartLevel();
             log.info("Start level is now {}", level);
             try {
-                // Simulate some resource discovery latency 
-                WaitFor.randomWait(56);
-                installMoreBundles();
+                installBundles(startLevelService.getStartLevel());
             } catch(Exception e) {
                 log.error("Installing bundles failed", e);
             }

Modified: sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/IncrementalStartupIT.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/IncrementalStartupIT.java?rev=1690063&r1=1690062&r2=1690063&view=diff
==============================================================================
--- sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/IncrementalStartupIT.java (original)
+++ sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/IncrementalStartupIT.java Thu Jul  9 11:05:58 2015
@@ -43,7 +43,7 @@ public class IncrementalStartupIT {
     private final Logger log = LoggerFactory.getLogger(getClass());
     public static final int DEFAULT_TIMEOUT = 5;
     private Map<String, Integer> startLevelMap;
-    private ServiceRegistration startLevelReportReg;
+    private ServiceRegistration startLevelMapReg;
     
     @Inject
     protected BundleContext bundleContext;
@@ -80,25 +80,21 @@ public class IncrementalStartupIT {
     
     private void setupReports() {
         startLevelMap = new HashMap<String, Integer>();
-        log.info("Registering StartLevelReport service");
-        startLevelReportReg = bundleContext.registerService(Map.class.getName(), startLevelMap, null);
+        startLevelMapReg = bundleContext.registerService(Map.class.getName(), startLevelMap, null);
     }
     
     private void cleanup() {
-        if(startLevelReportReg != null) {
-            log.info("Unregistering StartLevelReport service");
-            startLevelReportReg.unregister();
-            startLevelReportReg = null;
+        if(startLevelMapReg != null) {
+            startLevelMapReg.unregister();
+            startLevelMapReg = null;
         }
     }
     
     @Test
-    public void allBundlesActive() throws Exception {
+    public void testInstallAndActivate() throws Exception {
         setupReports();
+        final AsyncInstaller ai = new AsyncInstaller(bundleContext, installer, startLevel);
         try {
-            final AsyncInstaller ai = new AsyncInstaller(bundleContext, installer, startLevel);
-            ai.installMoreBundles();
-            
             final int from = startLevel.getStartLevel();
             final int to = from + P.START_LEVEL_CHANGE;
             for(int i=from; i <= to; i++) {
@@ -123,7 +119,8 @@ public class IncrementalStartupIT {
             };
 
             // No need for a WaitFor here, if the previous test passes all bundles
-            // are started
+            // are started. This checks whether our (intentionally slow) test bundle activators 
+            // finish excuting before the next start level change 
             new WaitFor(DEFAULT_TIMEOUT) {
                 protected boolean condition() {
                     final Collection<String> issues = ai.getActivatorIssues(startLevelMap);
@@ -135,6 +132,7 @@ public class IncrementalStartupIT {
                 }
             };
         } finally {
+            ai.cleanup();
             cleanup();
         }
     }

Modified: sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/P.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/P.java?rev=1690063&r1=1690062&r2=1690063&view=diff
==============================================================================
--- sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/P.java (original)
+++ sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/P.java Thu Jul  9 11:05:58 2015
@@ -31,8 +31,8 @@ import org.ops4j.pax.exam.options.Defaul
 /** Test parameters and configuration */
 public class P {
     
-    public static final int START_LEVEL_CHANGE = 5; 
-    public static final int MAX_BUNDLES_PER_LEVEL = 3;
+    public static final int START_LEVEL_CHANGE = 17; 
+    public static final int MAX_BUNDLES_PER_LEVEL = 39;
     public static final int MAX_WAIT_BETWEEN_LEVELS = 200;
     
     public static Option[] paxConfig() {

Modified: sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/SlowActivator.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/SlowActivator.java?rev=1690063&r1=1690062&r2=1690063&view=diff
==============================================================================
--- sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/SlowActivator.java (original)
+++ sling/whiteboard/bdelacretaz/startup-stuff/it-startup/src/test/java/org/apache/sling/launchpad/it/startup/SlowActivator.java Thu Jul  9 11:05:58 2015
@@ -19,6 +19,8 @@
 package org.apache.sling.launchpad.it.startup;
 
 import java.util.Map;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CountDownLatch;
 
 import org.osgi.framework.BundleActivator;
 import org.osgi.framework.BundleContext;
@@ -43,6 +45,7 @@ public class SlowActivator implements Bu
         WaitFor.sleep(msec);
 
         
+        // Get the current start level
         int level = -1;
         {
             final ServiceReference ref = context.getServiceReference(StartLevel.class.getName());
@@ -54,6 +57,7 @@ public class SlowActivator implements Bu
             }
         }
         
+        // Save our start level info to the test report map
         {
             final ServiceReference ref = context.getServiceReference(Map.class.getName());
             try {
@@ -63,6 +67,24 @@ public class SlowActivator implements Bu
             } finally {
                 context.ungetService(ref);
             }
+        }
+        
+        // And signal that we're done executing
+        {
+            final ServiceReference ref = context.getServiceReference(ConcurrentMap.class.getName());
+            try {
+                @SuppressWarnings("unchecked")
+                final Map<Integer, CountDownLatch> latchMap = (Map<Integer, CountDownLatch> )context.getService(ref);
+                final CountDownLatch cdl = latchMap.get(level);
+                if(cdl == null) {
+                    log.warn("CountDownLatch not found for start level {}", level);
+                } else {
+                    log.info("Activator done, decrementing CountDownLatch for level {}: {}", level, bsn);
+                    cdl.countDown();
+                }
+            } finally {
+                context.ungetService(ref);
+            }
         }
     }
 

Modified: sling/whiteboard/bdelacretaz/startup-stuff/startup-sequencer/src/main/java/org/apache/sling/startup/sequencer/StartLevelChangeBarrier.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/bdelacretaz/startup-stuff/startup-sequencer/src/main/java/org/apache/sling/startup/sequencer/StartLevelChangeBarrier.java?rev=1690063&r1=1690062&r2=1690063&view=diff
==============================================================================
--- sling/whiteboard/bdelacretaz/startup-stuff/startup-sequencer/src/main/java/org/apache/sling/startup/sequencer/StartLevelChangeBarrier.java (original)
+++ sling/whiteboard/bdelacretaz/startup-stuff/startup-sequencer/src/main/java/org/apache/sling/startup/sequencer/StartLevelChangeBarrier.java Thu Jul  9 11:05:58 2015
@@ -34,7 +34,4 @@ public interface StartLevelChangeBarrier
      * @return a latch or null if none is needed for this level  
      */
     CountDownLatch getStartLevelLatch(int startLevel);
-    
-    /** The textual description of this barrier */
-    String getDescription();
 }

Modified: sling/whiteboard/bdelacretaz/startup-stuff/startup-sequencer/src/main/java/org/apache/sling/startup/sequencer/impl/StartLevelControllerImpl.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/bdelacretaz/startup-stuff/startup-sequencer/src/main/java/org/apache/sling/startup/sequencer/impl/StartLevelControllerImpl.java?rev=1690063&r1=1690062&r2=1690063&view=diff
==============================================================================
--- sling/whiteboard/bdelacretaz/startup-stuff/startup-sequencer/src/main/java/org/apache/sling/startup/sequencer/impl/StartLevelControllerImpl.java (original)
+++ sling/whiteboard/bdelacretaz/startup-stuff/startup-sequencer/src/main/java/org/apache/sling/startup/sequencer/impl/StartLevelControllerImpl.java Thu Jul  9 11:05:58 2015
@@ -18,8 +18,13 @@
  */
 package org.apache.sling.startup.sequencer.impl;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sling.startup.sequencer.StartLevelChangeBarrier;
 import org.apache.sling.startup.sequencer.StartLevelController;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.startlevel.StartLevel;
 import org.slf4j.Logger;
@@ -33,6 +38,9 @@ public class StartLevelControllerImpl im
     private final Logger log = LoggerFactory.getLogger(getClass());
     private final BundleContext bundleContext;
     
+    // TODO configurable
+    public static final int LATCH_TIMEOUT_MSEC = 30 * 1000;
+    
     public StartLevelControllerImpl(BundleContext ctx) {
         this.bundleContext = ctx;
     }
@@ -43,13 +51,58 @@ public class StartLevelControllerImpl im
             throw new IllegalStateException("StartLevel service not found");
         }
         final StartLevel sl = (StartLevel)bundleContext.getService(ref);
+        final int currentLevel = sl.getStartLevel();
+        final int delta = targetLevel - currentLevel;
         
         try {
-            // TODO increment levels one by one and wait at each step
-            log.info("Setting start level to {}", targetLevel);
-            sl.setStartLevel(targetLevel);
+            if(delta == 0) {
+                log.info("Start level is already {}, change ignored", targetLevel);
+            } else if(delta < 0) {
+                log.info("Start level goes down {} -> {}, immediate change without sequencing", currentLevel, targetLevel);
+            } else {
+                log.info("Start level goes up {} -> {}, will use sequencing", currentLevel, targetLevel);
+                for(int level = currentLevel + 1; level <= targetLevel; level++) {
+                    sl.setStartLevel(level);
+                    waitForLatches(level);
+                }
+            }
         } finally {
             bundleContext.ungetService(ref);
         }
     }
+    
+    private void waitForLatches(int startLevel) {
+        ServiceReference [] refs = null;
+        try {
+            refs = bundleContext.getServiceReferences(StartLevelChangeBarrier.class.getName(), null);
+            if(refs == null) {
+                log.info("No StartLevelChangeBarrier services found, won't wait");
+            } else {
+                for(ServiceReference ref : refs) {
+                    final StartLevelChangeBarrier b = (StartLevelChangeBarrier)bundleContext.getService(ref);
+                    final CountDownLatch cdl = b.getStartLevelLatch(startLevel);
+                    if(cdl == null) {
+                        log.info("No CountDownLatch provided by {} at start level {}", b, startLevel);
+                    } else {
+                        log.info("Waiting on CountDownLatch({}) provided by {} at start level {}", 
+                                new Object[] {cdl.getCount(), b, startLevel});
+                        try {
+                            cdl.await(LATCH_TIMEOUT_MSEC, TimeUnit.MILLISECONDS);
+                            log.info("Done waiting, {} = {}", cdl, cdl.getCount());
+                        } catch(InterruptedException iex) {
+                            log.warn("InterruptedException while awaiting {}", cdl);
+                        }
+                    }
+                }
+            }
+        } catch (InvalidSyntaxException e) {
+            throw new RuntimeException("InvalidSyntaxException - should not happen??");
+        } finally {
+            if(refs != null) {
+                for(ServiceReference ref : refs) {
+                    bundleContext.ungetService(ref);
+                }
+            }
+        }
+    }
 }