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 2011/09/12 14:38:16 UTC

svn commit: r1169713 - in /sling/trunk/testing: samples/integration-tests/ tools/ tools/src/main/java/org/apache/sling/testing/tools/osgi/ tools/src/main/java/org/apache/sling/testing/tools/sling/

Author: bdelacretaz
Date: Mon Sep 12 12:38:15 2011
New Revision: 1169713

URL: http://svn.apache.org/viewvc?rev=1169713&view=rev
Log:
SLING-2001 and SLING-2197 - try to start all installed bundles repeatedly until success, to cope with inter-bundle dependencies

Added:
    sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/BundlesInstaller.java   (with props)
Modified:
    sling/trunk/testing/samples/integration-tests/pom.xml
    sling/trunk/testing/tools/pom.xml
    sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/osgi/WebconsoleClient.java
    sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/SlingTestBase.java
    sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/TimeoutsProvider.java

Modified: sling/trunk/testing/samples/integration-tests/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/testing/samples/integration-tests/pom.xml?rev=1169713&r1=1169712&r2=1169713&view=diff
==============================================================================
--- sling/trunk/testing/samples/integration-tests/pom.xml (original)
+++ sling/trunk/testing/samples/integration-tests/pom.xml Mon Sep 12 12:38:15 2011
@@ -212,6 +212,8 @@
                         <server.ready.path.1>/:script src="system/sling.js"</server.ready.path.1>
                         <server.ready.path.2>/.explorer.html:href="/libs/sling/explorer/css/explorer.css"</server.ready.path.2>
                         <server.ready.path.3>/sling-test/sling/sling-test.html:Sling client library tests</server.ready.path.3>
+                        <start.bundles.timeout.seconds>30</start.bundles.timeout.seconds>
+                        <bundle.install.timeout.seconds>20</bundle.install.timeout.seconds>
                         
                         <!-- 
                             Define additional bundles to install by specifying the beginning of their artifact name.

Modified: sling/trunk/testing/tools/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/testing/tools/pom.xml?rev=1169713&r1=1169712&r2=1169713&view=diff
==============================================================================
--- sling/trunk/testing/tools/pom.xml (original)
+++ sling/trunk/testing/tools/pom.xml Mon Sep 12 12:38:15 2011
@@ -66,6 +66,10 @@
 
     <dependencies>
         <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.apache.commons</groupId>
             <artifactId>commons-exec</artifactId>
             <version>1.1</version>
@@ -86,6 +90,11 @@
             <version>4.1</version>
         </dependency>
         <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.json</artifactId>
+            <version>2.0.6</version>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
             <version>1.5.11</version>

Modified: sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/osgi/WebconsoleClient.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/osgi/WebconsoleClient.java?rev=1169713&r1=1169712&r2=1169713&view=diff
==============================================================================
--- sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/osgi/WebconsoleClient.java (original)
+++ sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/osgi/WebconsoleClient.java Mon Sep 12 12:38:15 2011
@@ -16,14 +16,19 @@
  */
 package org.apache.sling.testing.tools.osgi;
 
+import static org.junit.Assert.fail;
+
 import java.io.File;
 
 import org.apache.http.entity.mime.MultipartEntity;
 import org.apache.http.entity.mime.content.FileBody;
 import org.apache.http.entity.mime.content.StringBody;
 import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.sling.commons.json.JSONArray;
+import org.apache.sling.commons.json.JSONObject;
 import org.apache.sling.testing.tools.http.RequestBuilder;
 import org.apache.sling.testing.tools.http.RequestExecutor;
+import org.apache.sling.testing.tools.http.RetryingContentChecker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -35,6 +40,10 @@ public class WebconsoleClient {
     private final String username;
     private final String password;
     
+    public static final String JSON_KEY_DATA = "data";
+    public static final String JSON_KEY_STATE = "state";
+    public static final String CONSOLE_BUNDLES_PATH = "/system/console/bundles";
+    
     public WebconsoleClient(String slingServerUrl, String username, String password) {
         this.builder = new RequestBuilder(slingServerUrl);
         this.executor = new RequestExecutor(new DefaultHttpClient());
@@ -44,7 +53,7 @@ public class WebconsoleClient {
     
     /** Install a bundle using the Felix webconsole HTTP interface */
     public void installBundle(File f, boolean startBundle) throws Exception {
-        log.info("Installing additional bundle {}", f.getName());
+        log.info("Installing bundle {}", f.getName());
         
         // Setup request for Felix Webconsole bundle install
         final MultipartEntity entity = new MultipartEntity();
@@ -57,9 +66,67 @@ public class WebconsoleClient {
         // Console returns a 302 on success (and in a POST this
         // is not handled automatically as per HTTP spec)
         executor.execute(
-                builder.buildPostRequest("/system/console/bundles")
+                builder.buildPostRequest(CONSOLE_BUNDLES_PATH)
                 .withCredentials(username, password)
                 .withEntity(entity)
         ).assertStatus(302);
     }
+    
+    /** Check that specified bundle is installed - must be called
+     *  before other methods that take a symbolicName parameter, 
+     *  in case installBundle was just called and the actual 
+     *  installation hasn't happened yet. */
+    public void checkBundleInstalled(String symbolicName, int timeoutSeconds) {
+        final String path = getBundlePath(symbolicName, ".json");
+        new RetryingContentChecker(executor, builder).check(path, 200, timeoutSeconds, 500);
+    }
+    
+    /** Get specified bundle state */
+    public String getBundleState(String symbolicName) throws Exception {
+        // This returns a data structure like
+        // {"status":"Bundle information: 173 bundles in total - all 173 bundles active.","s":[173,171,2,0,0],"data":
+        //  [
+        //      {"id":0,"name":"System Bundle","fragment":false,"stateRaw":32,"state":"Active","version":"3.0.7","symbolicName":"org.apache.felix.framework","category":""},
+        //  ]}
+        final String path = getBundlePath(symbolicName, ".json");
+        final String content = executor.execute(
+                builder.buildGetRequest(path)
+                .withCredentials(username, password)
+        ).assertStatus(200)
+        .getContent();
+        
+        final JSONObject root = new JSONObject(content);
+        if(!root.has(JSON_KEY_DATA)) {
+            fail(path + " does not provide '" + JSON_KEY_DATA + "' element, JSON content=" + content);
+        }
+        final JSONArray data = root.getJSONArray(JSON_KEY_DATA);
+        if(data.length() < 1) {
+            fail(path + "." + JSON_KEY_DATA + " is empty, JSON content=" + content);
+        }
+        final JSONObject bundle = data.getJSONObject(0);
+        if(!bundle.has(JSON_KEY_STATE)) {
+            fail(path + ".data[0].state missing, JSON content=" + content);
+        }
+        return bundle.getString(JSON_KEY_STATE);
+    }
+    
+    /** Start specified bundle */
+    public void startBundle(String symbolicName) throws Exception {
+        // To start the bundle we POST action=start to its URL
+        final String path = getBundlePath(symbolicName, null);
+        log.info("Starting bundle {} via {}", symbolicName, path);
+        
+        final MultipartEntity entity = new MultipartEntity();
+        entity.addPart("action",new StringBody("start"));
+        executor.execute(
+                builder.buildPostRequest(path)
+                .withCredentials(username, password)
+                .withEntity(entity)
+        ).assertStatus(200);
+    }
+    
+    private String getBundlePath(String symbolicName, String extension) {
+        return CONSOLE_BUNDLES_PATH + "/" + symbolicName 
+        + (extension == null ? "" : extension);
+    }
 }

Added: sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/BundlesInstaller.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/BundlesInstaller.java?rev=1169713&view=auto
==============================================================================
--- sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/BundlesInstaller.java (added)
+++ sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/BundlesInstaller.java Mon Sep 12 12:38:15 2011
@@ -0,0 +1,108 @@
+/*
+ * 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.testing.tools.sling;
+
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.apache.sling.testing.tools.osgi.WebconsoleClient;
+import org.osgi.framework.Constants;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/** Utility that installs and starts additional bundles for testing */ 
+public class BundlesInstaller {
+    private final Logger log = LoggerFactory.getLogger(getClass());
+    private final WebconsoleClient webconsoleClient;
+    public static final String ACTIVE_STATE = "active";
+    
+    public BundlesInstaller(WebconsoleClient wcc) {
+        webconsoleClient = wcc;
+    }
+    
+    /** Install a list of bundles supplied as Files */
+    public void installBundles(List<File> toInstall, boolean startBundles) throws Exception {
+        for(File f : toInstall) {
+            webconsoleClient.installBundle(f, startBundles);
+        }
+        log.info("{} additional bundles installed from {}", toInstall.size(), toInstall.get(0).getAbsolutePath());
+    }
+    
+    /** Wait for all bundles specified in symbolicNames list to be installed in the
+     *  remote web console.
+     */
+    public void waitForBundlesInstalled(List<String> symbolicNames, int timeoutSeconds) throws Exception {
+        log.info("Checking that bundles are installed (timeout {} seconds): {}", timeoutSeconds, symbolicNames);
+        for(String symbolicName : symbolicNames) {
+            webconsoleClient.checkBundleInstalled(symbolicName, timeoutSeconds);
+        }
+    }
+    
+    public void startAllBundles(List<String> symbolicNames, int timeoutSeconds) throws Exception {
+        log.info("Starting bundles (timeout {} seconds): {}", timeoutSeconds, symbolicNames);
+        
+        final long timeout = System.currentTimeMillis() + timeoutSeconds * 1000L;
+        final List<String> toStart = new LinkedList<String>();
+        while(System.currentTimeMillis() < timeout) {
+            toStart.clear();
+            for(String name : symbolicNames) {
+                final String state = webconsoleClient.getBundleState(name);
+                if(!state.equalsIgnoreCase(ACTIVE_STATE)) {
+                    toStart.add(name);
+                    break;
+                }
+            }
+            
+            if(toStart.isEmpty()) {
+                log.info("Ok - all bundles are in the {} state", ACTIVE_STATE);
+                break;
+            }
+            
+            for(String name : toStart) {
+                webconsoleClient.startBundle(name);
+            }
+            
+            Thread.sleep(500L);
+        }
+        
+        if(!toStart.isEmpty()) {
+            fail("Some bundles did not start: " + toStart);
+        }
+    }
+    
+    public String getBundleSymbolicName(File bundleFile) throws IOException {
+        String name = null;
+        final JarInputStream jis = new JarInputStream(new FileInputStream(bundleFile));
+        try {
+            final Manifest m = jis.getManifest();
+            if(m == null) {
+                fail("Manifest is null in " + bundleFile.getAbsolutePath());
+            }
+            name = m.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+        } finally {
+            jis.close();
+        }
+        return name;
+    }
+}
\ No newline at end of file

Propchange: sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/BundlesInstaller.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/BundlesInstaller.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/SlingTestBase.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/SlingTestBase.java?rev=1169713&r1=1169712&r2=1169713&view=diff
==============================================================================
--- sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/SlingTestBase.java (original)
+++ sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/SlingTestBase.java Mon Sep 12 12:38:15 2011
@@ -21,6 +21,7 @@ import static org.junit.Assert.fail;
 import java.io.File;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.TreeSet;
 
@@ -44,22 +45,27 @@ public class SlingTestBase {
     public static final String SERVER_HOSTNAME_PROP = "test.server.hostname";
     public static final String ADDITONAL_BUNDLES_PATH = "additional.bundles.path";
     public static final String BUNDLE_TO_INSTALL_PREFIX = "sling.additional.bundle";
+    public static final String START_BUNDLES_TIMEOUT_SECONDS = "start.bundles.timeout.seconds";
+    public static final String BUNDLE_INSTALL_TIMEOUT_SECONDS = "bundle.install.timeout.seconds";
     public static final String ADMIN = "admin";
     
     private static final boolean keepJarRunning = "true".equals(System.getProperty(KEEP_JAR_RUNNING_PROP));
     private final String serverBaseUrl;
-    private static RequestBuilder builder;
-    private static DefaultHttpClient httpClient = new DefaultHttpClient();
-    private static RequestExecutor executor = new RequestExecutor(httpClient);
-    private static WebconsoleClient webconsoleClient;
+    private RequestBuilder builder;
+    private DefaultHttpClient httpClient = new DefaultHttpClient();
+    private RequestExecutor executor = new RequestExecutor(httpClient);
+    private WebconsoleClient webconsoleClient;
+    private BundlesInstaller bundlesInstaller;
     private static boolean serverStarted;
     private static boolean serverStartedByThisClass;
     private static boolean serverReady;
     private static boolean serverReadyTestFailed;
+    private static boolean installBundlesFailed;
     private static boolean extraBundlesInstalled;
     private static boolean startupInfoProvided;
+    private static boolean serverInfoLogged;
 
-    private static final Logger log = LoggerFactory.getLogger(SlingTestBase.class);
+    private final Logger log = LoggerFactory.getLogger(getClass());
     private static JarExecutor jarExecutor;
     
     /** Get configuration but do not start server yet, that's done on demand */
@@ -87,10 +93,15 @@ public class SlingTestBase {
             serverBaseUrl = "http://" + serverHost + ":" + jarExecutor.getServerPort();
         }
 
-        log.info("Server base URL={}", serverBaseUrl);
         builder = new RequestBuilder(serverBaseUrl);
         webconsoleClient = new WebconsoleClient(serverBaseUrl, ADMIN, ADMIN);
         builder = new RequestBuilder(serverBaseUrl);
+        bundlesInstaller = new BundlesInstaller(webconsoleClient);
+        
+        if(!serverInfoLogged) {
+            log.info("Server base URL={}", serverBaseUrl);
+            serverInfoLogged = true;
+        }
     }
 
     /** Start the server, if not done yet */
@@ -110,7 +121,7 @@ public class SlingTestBase {
             }
             startupInfoProvided = true;
             waitForServerReady();
-            installExtraBundles();
+            installAdditionalBundles();
             blockIfRequested();
         } catch(Exception e) {
             log.error("Exception in maybeStartServer()", e);
@@ -118,6 +129,45 @@ public class SlingTestBase {
         }
     }
     
+    protected void installAdditionalBundles() {
+        if(installBundlesFailed) {
+            fail("Bundles could not be installed, cannot run tests");
+        } else if(!extraBundlesInstalled) {
+            final String path = System.getProperty(ADDITONAL_BUNDLES_PATH);
+            if(path == null) {
+                log.info("System property {} not set, additional bundles won't be installed", 
+                        ADDITONAL_BUNDLES_PATH);
+            } else {
+                final List<File> toInstall = getBundlesToInstall(path);
+                
+                try {
+                    // Install bundles, check that they are installed and start them all
+                    bundlesInstaller.installBundles(toInstall, false);
+                    final List<String> symbolicNames = new LinkedList<String>();
+                    for(File f : toInstall) {
+                        symbolicNames.add(bundlesInstaller.getBundleSymbolicName(f));
+                    }
+                    bundlesInstaller.waitForBundlesInstalled(symbolicNames, 
+                            TimeoutsProvider.getInstance().getTimeout(BUNDLE_INSTALL_TIMEOUT_SECONDS, 10));
+                    bundlesInstaller.startAllBundles(symbolicNames,
+                            TimeoutsProvider.getInstance().getTimeout(START_BUNDLES_TIMEOUT_SECONDS, 30));
+                } catch(AssertionError ae) {
+                    log.info("Exception while installing additional bundles", ae);
+                    installBundlesFailed = true;
+                } catch(Exception e) {
+                    log.info("Exception while installing additional bundles", e);
+                    installBundlesFailed = true;
+                }
+                
+                if(installBundlesFailed) {
+                    fail("Could not start all installed bundles:" + toInstall);
+                }
+            }
+        }
+        
+        extraBundlesInstalled = !installBundlesFailed;
+    }
+    
     /** Start server if needed, and return a RequestBuilder that points to it */
     protected RequestBuilder getRequestBuilder() {
         startServerIfNeeded();
@@ -215,29 +265,17 @@ public class SlingTestBase {
         }
     }
     
-    /** Install all bundles found under our additional bundles path */
-    protected void installExtraBundles() throws Exception {
-        if(extraBundlesInstalled) {
-            return;
-        }
-        extraBundlesInstalled = true;
-        
-        if(!serverStartedByThisClass) {
-            log.info("Server was not started here, additional bundles will not be installed");
-            return;
+    /** Get the list of additional bundles to install, as specified by path parameter */ 
+    protected List<File> getBundlesToInstall(String additionalBundlesPath) {
+        final List<File> result = new LinkedList<File>(); 
+        if(additionalBundlesPath == null) {
+            return result;
         }
         
-        final String path = System.getProperty(ADDITONAL_BUNDLES_PATH);
-        if(path == null) {
-            log.info("System property {} not set, additional bundles won't be installed", 
-                    ADDITONAL_BUNDLES_PATH);
-            return;
-        }
-        
-        final File dir = new File(path);
+        final File dir = new File(additionalBundlesPath);
         if(!dir.isDirectory() || !dir.canRead()) {
             log.info("Cannot read additional bundles directory {}, ignored", dir.getAbsolutePath());
-            return;
+            return result;
         }
         
         // Collect all filenames of candidate bundles
@@ -251,8 +289,7 @@ public class SlingTestBase {
             }
         }
         
-        // And install those that are specified by system properties, in order
-        int count = 0;
+        // We'll install those that are specified by system properties, in order
         final List<String> sortedPropertyKeys = new ArrayList<String>();
         for(Object key : System.getProperties().keySet()) {
             final String str = key.toString();
@@ -265,15 +302,14 @@ public class SlingTestBase {
             final String filenamePrefix = System.getProperty(key);
             for(String bundleFilename : bundleNames) {
                 if(bundleFilename.startsWith(filenamePrefix)) {
-                    webconsoleClient.installBundle(new File(dir, bundleFilename), true);
-                    count++;
+                    result.add(new File(dir, bundleFilename));
                 }
             }
         }
         
-        log.info("{} additional bundles installed from {}", count, dir.getAbsolutePath());
+        return result;
     }
- 
+    
     protected boolean isServerStartedByThisClass() {
         return serverStartedByThisClass;
     }

Modified: sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/TimeoutsProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/TimeoutsProvider.java?rev=1169713&r1=1169712&r2=1169713&view=diff
==============================================================================
--- sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/TimeoutsProvider.java (original)
+++ sling/trunk/testing/tools/src/main/java/org/apache/sling/testing/tools/sling/TimeoutsProvider.java Mon Sep 12 12:38:15 2011
@@ -64,4 +64,14 @@ public class TimeoutsProvider {
         final int result = (int)(nomimalValue * timeoutFactor);
         return result;
     }
+    
+    /** Get timeout from a system property, with default value */
+    public int getTimeout(String systemPropertyName, int defaultNominalValue) {
+        int result = defaultNominalValue;
+        final String str = System.getProperty(systemPropertyName);
+        if(str != null) {
+            result = Integer.parseInt(str);
+        }
+        return getTimeout(result);
+    }
 }