You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ace.apache.org by ma...@apache.org on 2009/06/27 17:53:26 UTC

svn commit: r788992 [25/25] - in /incubator/ace/trunk: gateway/ gateway/src/ gateway/src/net/ gateway/src/net/luminis/ gateway/src/net/luminis/liq/ gateway/src/net/luminis/liq/bootstrap/ gateway/src/net/luminis/liq/bootstrap/multigateway/ gateway/src/n...

Added: incubator/ace/trunk/test/src/net/luminis/liq/test/useradminconfigurator/Activator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/test/src/net/luminis/liq/test/useradminconfigurator/Activator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/test/src/net/luminis/liq/test/useradminconfigurator/Activator.java (added)
+++ incubator/ace/trunk/test/src/net/luminis/liq/test/useradminconfigurator/Activator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,84 @@
+package net.luminis.liq.test.useradminconfigurator;
+
+import java.io.IOException;
+import java.util.Properties;
+
+import net.luminis.liq.repository.Repository;
+import net.luminis.liq.repository.impl.constants.RepositoryConstants;
+import net.luminis.test.osgi.dm.TestActivatorBase;
+
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.cm.Configuration;
+import org.osgi.service.cm.ConfigurationAdmin;
+import org.osgi.service.useradmin.UserAdmin;
+
+public class Activator extends TestActivatorBase {
+
+    private volatile ConfigurationAdmin m_configAdmin;
+
+    @Override
+    protected void initServices(BundleContext context, DependencyManager manager) {
+        manager.add(createService()
+                .setImplementation(ConfiguratorTest.class)
+                .add(createServiceDependency()
+                        .setService(UserAdmin.class)
+                        .setRequired(true))
+                .add(createServiceDependency()
+                    .setService(Repository.class, "(&(" + RepositoryConstants.REPOSITORY_NAME + "=users)(" + RepositoryConstants.REPOSITORY_CUSTOMER + "=luminis))")
+                    .setRequired(true)));
+
+        // We need to do some configuration for this test to run; therefore,
+        // we (as activator) wait around for the ConfigurationAdmin.
+        manager.add(createService()
+                .setImplementation(this)
+                .add(createServiceDependency()
+                        .setService(ConfigurationAdmin.class)
+                        .setRequired(true)));
+
+    }
+
+    @Override
+    protected Class[] getTestClasses() {
+        return new Class[] { ConfiguratorTest.class };
+    }
+
+    public void start() throws IOException {
+        // Create the repository
+        Configuration config = m_configAdmin.createFactoryConfiguration("net.luminis.liq.server.repository.factory", null);
+
+        Properties props = new Properties();
+        props.put(RepositoryConstants.REPOSITORY_NAME, "users");
+        props.put(RepositoryConstants.REPOSITORY_CUSTOMER, "luminis");
+        props.put(RepositoryConstants.REPOSITORY_MASTER, "true");
+
+        config.update(props);
+
+        // Start the servlet
+        config = m_configAdmin.getConfiguration("net.luminis.liq.repository.servlet.RepositoryServlet", null);
+
+        props = new Properties();
+        props.put("net.luminis.liq.server.servlet.endpoint", "/repository");
+
+        config.update(props);
+
+        // Configure the task
+        config = m_configAdmin.getConfiguration("net.luminis.liq.configurator.useradmin.task.UpdateUserAdminTask", null);
+
+        props = new Properties();
+        props.put("repositoryName", "users");
+        props.put("repositoryCustomer", "luminis");
+        props.put("repositoryLocation", "http://localhost:8080/repository");
+
+        config.update(props);
+
+        // Schedule the task
+        config = m_configAdmin.getConfiguration("net.luminis.liq.scheduler", null);
+
+        props = new Properties();
+        props.put("net.luminis.liq.configurator.useradmin.task.UpdateUserAdminTask", "1000");
+
+        config.update(props);
+    }
+
+}

Added: incubator/ace/trunk/test/src/net/luminis/liq/test/useradminconfigurator/ConfiguratorTest.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/test/src/net/luminis/liq/test/useradminconfigurator/ConfiguratorTest.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/test/src/net/luminis/liq/test/useradminconfigurator/ConfiguratorTest.java (added)
+++ incubator/ace/trunk/test/src/net/luminis/liq/test/useradminconfigurator/ConfiguratorTest.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,84 @@
+package net.luminis.liq.test.useradminconfigurator;
+
+import static net.luminis.liq.test.utils.TestUtils.INTEGRATION;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import net.luminis.liq.repository.Repository;
+
+import org.osgi.service.useradmin.User;
+import org.osgi.service.useradmin.UserAdmin;
+import org.testng.annotations.Factory;
+import org.testng.annotations.Test;
+
+public class ConfiguratorTest {
+
+    public static Object m_instance;
+
+    private volatile Repository m_repository;
+    private volatile UserAdmin m_userAdmin;
+
+    public ConfiguratorTest() {
+        synchronized (ConfiguratorTest.class) {
+            if (m_instance == null) {
+                m_instance = this;
+            }
+        }
+    }
+
+    @Factory
+    public Object[] createInstances() {
+        synchronized (ConfiguratorTest.class) {
+            return new Object[] { m_instance };
+        }
+    }
+
+    /**
+     * Creates a file in the repository, and waits for the UserAdmin to have a new user
+     * present, and inspects that user.
+     */
+    @Test(groups = { INTEGRATION })
+    public void configuratorTest() throws IllegalArgumentException, IOException, InterruptedException {
+        ByteArrayInputStream bis = new ByteArrayInputStream((
+            "<roles>" +
+            "    <user name=\"TestUser\">" +
+            "    <properties>" +
+            "        <email>testUser@luminis.nl</email>" +
+            "    </properties>" +
+            "    <credentials>" +
+            "        <password type=\"String\">swordfish</password>" +
+            "        <certificate type=\"byte[]\">42</certificate>" +
+            "    </credentials>" +
+            "    </user>" +
+            "</roles>").getBytes());
+
+        m_repository.commit(bis, m_repository.getRange().getHigh());
+
+        User user = (User) m_userAdmin.getRole("TestUser");
+        int count = 0;
+        while ((user == null) && (count < 16)) {
+            Thread.sleep(250);
+            user = (User) m_userAdmin.getRole("TestUser");
+            count++;
+        }
+        if (user == null) {
+            assert false : "Even after four seconds, our user is not present.";
+        }
+
+        boolean foundPassword = false;
+        boolean foundCertificate = false;
+        count = 0;
+        while (!foundPassword & !foundCertificate && (count < 20)) {
+            // Note: there is a window between the creation of the user and the setting of the properties.
+            Thread.sleep(50);
+            foundPassword = user.hasCredential("password", "swordfish");
+            foundCertificate = user.hasCredential("certificate", new byte[] {'4', '2'});
+        }
+
+        assert foundPassword : "A second after our user becoming available, there is no password.";
+        assert foundCertificate : "A second after our user becoming available, there is no certificate.";
+    }
+
+
+}

Added: incubator/ace/trunk/test/src/net/luminis/liq/test/utils/FileUtils.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/test/src/net/luminis/liq/test/utils/FileUtils.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/test/src/net/luminis/liq/test/utils/FileUtils.java (added)
+++ incubator/ace/trunk/test/src/net/luminis/liq/test/utils/FileUtils.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,45 @@
+package net.luminis.liq.test.utils;
+
+import java.io.File;
+import java.io.IOException;
+
+public class FileUtils {
+
+    /**
+     * Convenience method for creating temp files.
+     * It creates a temp file, and then deletes it. This is done so the same (unique) filename can be used to create a directory.
+     *
+     * If you use null as the baseDirectoryName, a tempfile is created in the platform specific temp directory.
+     * @throws IOException
+     */
+    public static File createTempFile(File baseDirectory) throws IOException {
+        return createTempFile(baseDirectory, "");
+    }
+
+    public static File createTempFile(File baseDirectory, String extension) throws IOException {
+        File tempFile = File.createTempFile("test", extension, baseDirectory);
+        tempFile.delete();
+        return tempFile;
+    }
+
+    /**
+     * Remove the given directory and all it's files and subdirectories
+     * @param directory the name of the directory to remove
+     */
+    public static void removeDirectoryWithContent(File directory) {
+        if ((directory == null) || !directory.exists()) {
+            return;
+        }
+        File[] filesAndSubDirs = directory.listFiles();
+        for (int i=0; i < filesAndSubDirs.length; i++) {
+            File file = filesAndSubDirs[i];
+            if (file.isDirectory()) {
+                removeDirectoryWithContent(file);
+            }
+            // else just remove the file
+            file.delete();
+        }
+        directory.delete();
+    }
+
+}

Added: incubator/ace/trunk/test/src/net/luminis/liq/test/utils/NetUtils.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/test/src/net/luminis/liq/test/utils/NetUtils.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/test/src/net/luminis/liq/test/utils/NetUtils.java (added)
+++ incubator/ace/trunk/test/src/net/luminis/liq/test/utils/NetUtils.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,47 @@
+package net.luminis.liq.test.utils;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+/**
+ * Class containing utility methods concerning network related stuff.
+ */
+public class NetUtils {
+
+    /**
+     * Waits for a HTTP URL to become 'available', will retry every 100 milliseconds until it is available or timeout
+     * has been exceeded. Available in this context means the specified status code is returned when accessing the URL.
+     *
+     * @param url HTTP URL that should be tested for availability.
+     * @param responseCode The response code to be expected on the specified URL when it is available.
+     * @param timeout Amount of milliseconds to keep trying to access the URL.
+     * @return True if the response of the URL has the specified status code within the specified timeout delay, false otherwise.
+     * @throws IllegalArgumentException If the specified URL does not use the HTTP protocol.
+     */
+    public static boolean waitForURL(URL url, int responseCode, int timeout) {
+        long deadline = System.currentTimeMillis() + timeout;
+        while (System.currentTimeMillis() < deadline) {
+            try {
+                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+                connection.connect();
+                if (connection.getResponseCode() == responseCode) {
+                    return true;
+                }
+            } catch (ClassCastException cce) {
+                throw new IllegalArgumentException("Expected url to be an HTTP url, not: " + url.toString(), cce);
+            }
+            catch (IOException ioe) {
+                // retry
+            }
+            try {
+                Thread.sleep(100);
+            }
+            catch (InterruptedException ie) {
+                return false;
+            }
+        }
+        return false;
+    }
+
+}

Added: incubator/ace/trunk/test/src/net/luminis/liq/test/utils/TestUtils.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/test/src/net/luminis/liq/test/utils/TestUtils.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/test/src/net/luminis/liq/test/utils/TestUtils.java (added)
+++ incubator/ace/trunk/test/src/net/luminis/liq/test/utils/TestUtils.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,148 @@
+package net.luminis.liq.test.utils;
+
+import java.lang.reflect.AccessibleObject;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+/**
+ * Utility class that injects dependencies. Can be used to unit test service implementations.
+ */
+public class TestUtils {
+    public static final String UNIT = "unit";
+    public static final String INTEGRATION = "integration";
+    public static final String SMOKE = "smoke";
+    public static final String PERFORMANCE = "performance";
+    public static final String UI = "ui";
+    public static final String BROKEN = "broken";
+    /**
+     * Configures an object to use a null object for the specified service interface.
+     *
+     * @param object the object
+     * @param iface the service interface
+     */
+    public static <T> void configureObject(Object object, Class<T> iface) {
+        configureObject(object, iface, createNullObject(iface));
+    }
+
+    /**
+     * Creates a null object for a service interface.
+     *
+     * @param iface the service interface
+     * @return a null object
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T createNullObject(Class<T> iface) {
+        return (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new NullObject());
+    }
+
+    /**
+     * Wraps the given handler in an adapter that will try to pass on received invocations to the hander if that has
+     * an applicable methods else it defaults to a NullObject.
+     *
+     * @param iface the service interface
+     * @param handler the handler to pass invocations to.
+     * @return an adapter that will try to pass on received invocations to the given handler
+     */
+    @SuppressWarnings("unchecked")
+    public static <T> T createMockObjectAdapter(Class<T> iface, final Object handler) {
+        return (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new NullObject() {
+
+            @Override
+            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+                try {
+                    Method bridge = handler.getClass().getMethod(method.getName(), method.getParameterTypes());
+                    bridge.setAccessible(true);
+                    return bridge.invoke(handler, args);
+                }
+                catch (NoSuchMethodException ex) {
+                    return super.invoke(proxy, method, args);
+                }
+                catch (InvocationTargetException ex) {
+                    throw ex.getCause();
+                }
+            }
+        });
+    }
+
+    /**
+     * Configures an object to use a specific implementation for the specified service interface.
+     *
+     * @param object the object
+     * @param iface the service interface
+     * @param instance the implementation
+     */
+    @SuppressWarnings("unchecked")
+    public static void configureObject(Object object, Class iface, Object instance) {
+        Class serviceClazz = object.getClass();
+
+        while (serviceClazz != null) {
+            Field[] fields = serviceClazz.getDeclaredFields();
+            AccessibleObject.setAccessible(fields, true);
+            for (int j = 0; j < fields.length; j++) {
+                if (fields[j].getType().equals(iface)) {
+                    try {
+                        // synchronized makes sure the field is actually written to immediately
+                        synchronized (new Object()) {
+                            fields[j].set(object, instance);
+                        }
+                    }
+                    catch (Exception e) {
+                        throw new IllegalStateException("Could not set field " + fields[j].getName() + " on " + object);
+                    }
+                }
+            }
+            serviceClazz = serviceClazz.getSuperclass();
+        }
+    }
+
+    static class NullObject implements InvocationHandler {
+        private static final Boolean DEFAULT_BOOLEAN = Boolean.FALSE;
+
+        private static final Byte DEFAULT_BYTE = new Byte((byte) 0);
+
+        private static final Short DEFAULT_SHORT = new Short((short) 0);
+
+        private static final Integer DEFAULT_INT = new Integer(0);
+
+        private static final Long DEFAULT_LONG = new Long(0);
+
+        private static final Float DEFAULT_FLOAT = new Float(0.0f);
+
+        private static final Double DEFAULT_DOUBLE = new Double(0.0);
+
+        /**
+         * Invokes a method on this null object. The method will return a default value without doing anything.
+         */
+        @SuppressWarnings("unchecked")
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            Class returnType = method.getReturnType();
+            if (returnType.equals(Boolean.class) || returnType.equals(Boolean.TYPE)) {
+                return DEFAULT_BOOLEAN;
+            }
+            else if (returnType.equals(Byte.class) || returnType.equals(Byte.TYPE)) {
+                return DEFAULT_BYTE;
+            }
+            else if (returnType.equals(Short.class) || returnType.equals(Short.TYPE)) {
+                return DEFAULT_SHORT;
+            }
+            else if (returnType.equals(Integer.class) || returnType.equals(Integer.TYPE)) {
+                return DEFAULT_INT;
+            }
+            else if (returnType.equals(Long.class) || returnType.equals(Long.TYPE)) {
+                return DEFAULT_LONG;
+            }
+            else if (returnType.equals(Float.class) || returnType.equals(Float.TYPE)) {
+                return DEFAULT_FLOAT;
+            }
+            else if (returnType.equals(Double.class) || returnType.equals(Double.TYPE)) {
+                return DEFAULT_DOUBLE;
+            }
+            else {
+                return null;
+            }
+        }
+    }
+}

Added: incubator/ace/trunk/test/src/net/luminis/liq/test/utils/deployment/BundleStreamGenerator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/test/src/net/luminis/liq/test/utils/deployment/BundleStreamGenerator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/test/src/net/luminis/liq/test/utils/deployment/BundleStreamGenerator.java (added)
+++ incubator/ace/trunk/test/src/net/luminis/liq/test/utils/deployment/BundleStreamGenerator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,51 @@
+package net.luminis.liq.test.utils.deployment;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import net.luminis.liq.deployment.provider.ArtifactData;
+
+import org.osgi.framework.Constants;
+
+public class BundleStreamGenerator {
+
+    public static Manifest getBundleManifest(String symbolicname, String version, Map<String, String> additionalHeaders) {
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().putValue("Manifest-Version", "1");
+        manifest.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
+        manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, symbolicname);
+        manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION, version.toString());
+        for (Map.Entry<String, String> entry : additionalHeaders.entrySet()) {
+            manifest.getMainAttributes().putValue(entry.getKey(), entry.getValue());
+        }
+        return manifest;
+    }
+
+    public static void generateBundle(ArtifactData data, Map<String, String> additionalHeaders) throws IOException {
+        OutputStream bundleStream = null;
+        try {
+            File dataFile = new File(data.getUrl().toURI());
+            OutputStream fileStream = new FileOutputStream(dataFile);
+            bundleStream = new JarOutputStream(fileStream, getBundleManifest(data.getSymbolicName(), data.getVersion(), additionalHeaders));
+            bundleStream.flush();
+        } catch (URISyntaxException e) {
+            throw new IOException();
+        } finally {
+            if (bundleStream != null) {
+                bundleStream.close();
+            }
+        }
+    }
+
+    public static void generateBundle(ArtifactData data) throws IOException {
+        generateBundle(data, new HashMap<String, String>());
+    }
+
+}

Added: incubator/ace/trunk/test/src/net/luminis/liq/test/utils/deployment/TestData.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/test/src/net/luminis/liq/test/utils/deployment/TestData.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/test/src/net/luminis/liq/test/utils/deployment/TestData.java (added)
+++ incubator/ace/trunk/test/src/net/luminis/liq/test/utils/deployment/TestData.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,71 @@
+package net.luminis.liq.test.utils.deployment;
+
+import java.net.URL;
+import java.util.jar.Attributes;
+
+import net.luminis.liq.deployment.provider.ArtifactData;
+
+public class TestData implements ArtifactData {
+    private final String m_fileName;
+
+    private final String m_symbolicName;
+
+    private final URL m_url;
+
+    private final String m_version;
+
+    private final boolean m_changed;
+
+    public TestData(String fileName, String symbolicName, URL url, String version, boolean changed) {
+        m_fileName = fileName;
+        m_symbolicName = symbolicName;
+        m_url = url;
+        m_version = version;
+        m_changed = changed;
+    }
+
+    public boolean hasChanged() {
+        return m_changed;
+    }
+
+    public String getFilename() {
+        return m_fileName;
+    }
+
+    public String getSymbolicName() {
+        return m_symbolicName;
+    }
+
+    public URL getUrl() {
+        return m_url;
+    }
+
+    public String getVersion() {
+        return m_version;
+    }
+
+    public String getDirective() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public Attributes getManifestAttributes(boolean fixPackage) {
+        Attributes a = new Attributes();
+        a.putValue("Bundle-SymbolicName", getSymbolicName());
+        a.putValue("Bundle-Version", getVersion());
+        return a;
+    }
+
+    public String getProcessorPid() {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public boolean isBundle() {
+        return true;
+    }
+
+    public boolean isCustomizer() {
+        return false;
+    }
+}

Added: incubator/ace/trunk/test/src/net/luminis/liq/test/utils/deployment/TestProvider.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/test/src/net/luminis/liq/test/utils/deployment/TestProvider.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/test/src/net/luminis/liq/test/utils/deployment/TestProvider.java (added)
+++ incubator/ace/trunk/test/src/net/luminis/liq/test/utils/deployment/TestProvider.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,41 @@
+package net.luminis.liq.test.utils.deployment;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import net.luminis.liq.deployment.provider.ArtifactData;
+import net.luminis.liq.deployment.provider.DeploymentProvider;
+
+public class TestProvider implements DeploymentProvider {
+    private List<ArtifactData> m_collection;
+    private List<String> m_versions;
+
+    public TestProvider() throws Exception {
+        m_collection = new ArrayList<ArtifactData>();
+        m_versions = new ArrayList<String>();
+    }
+
+    public void addData(String fileName, String symbolicName, URL url, String version) {
+        addData(fileName, symbolicName, url, version, true);
+    }
+
+    public void addData(String fileName, String symbolicName, URL url, String version, boolean changed) {
+        m_collection.add(new TestData(fileName, symbolicName, url, version, changed));
+        m_versions.add(version);
+    }
+
+    public List<ArtifactData> getBundleData(String id, String version) {
+        return m_collection;
+    }
+
+    public List<ArtifactData> getBundleData(String id, String versionFrom, String versionTo) {
+        return m_collection;
+    }
+
+    public List<String> getVersions(String id) throws IllegalArgumentException {
+        Collections.sort(m_versions);
+        return m_versions;
+    }
+}

Added: incubator/ace/trunk/test/src/net/luminis/test/build/BrokenTestListener.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/test/src/net/luminis/test/build/BrokenTestListener.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/test/src/net/luminis/test/build/BrokenTestListener.java (added)
+++ incubator/ace/trunk/test/src/net/luminis/test/build/BrokenTestListener.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,49 @@
+package net.luminis.test.build;
+
+import java.util.Collection;
+
+import net.luminis.liq.test.utils.TestUtils;
+
+import org.testng.ITestContext;
+import org.testng.ITestNGMethod;
+import org.testng.Reporter;
+import org.testng.TestListenerAdapter;
+
+/**
+ * Reports information about broken tests.
+ */
+public class BrokenTestListener extends TestListenerAdapter {
+
+    @Override
+    public void onStart(ITestContext testContext) {
+        Collection<ITestNGMethod> excluded = testContext.getExcludedMethods();
+        StringBuffer output = new StringBuffer();
+        output.append("<h2>Broken tests</h2>\n");
+        output.append("<table><tr><td>Name</td><td>Class</td><td>Groups</td><td>Description</td></tr>");
+        for (ITestNGMethod m : excluded) {
+            StringBuffer groups = new StringBuffer();
+            boolean found = false;
+            for (String g : m.getGroups()) {
+                if (TestUtils.BROKEN.equals(g)) {
+                    found = true;
+                }
+                else {
+                    if (groups.length() > 0) {
+                        groups.append(", ");
+                    }
+                    groups.append(g);
+                }
+            }
+            if (found) {
+                output.append("<tr>" +
+            		"<td>" + m.getMethodName() + "</td>" +
+    				"<td>" + m.getMethod().getDeclaringClass().getName() + "</td>" +
+                    "<td>" + groups.toString() + "</td>" +
+                    "<td>" + m.getDescription() + "</td>" +
+					"</tr>\n");
+            }
+        }
+        output.append("</table>\n");
+        Reporter.log(output.toString());
+    }
+}

Added: incubator/ace/trunk/test/src/net/luminis/test/osgi/dm/FailTests.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/test/src/net/luminis/test/osgi/dm/FailTests.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/test/src/net/luminis/test/osgi/dm/FailTests.java (added)
+++ incubator/ace/trunk/test/src/net/luminis/test/osgi/dm/FailTests.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,36 @@
+package net.luminis.test.osgi.dm;
+
+import org.testng.annotations.Test;
+
+/**
+ * A test class that will always fail. It is used as a dummy class to signal that
+ * in fact the whole suite of tests did not run. This is usually caused by a test
+ * environment not starting up completely (such as dependencies failing).
+ */
+public class FailTests {
+    @SuppressWarnings("unchecked")
+    private static Class[] CLASSES;
+    private static String REASON;
+
+    @SuppressWarnings("unchecked")
+    public static void setClasses(Class[] classes) {
+        CLASSES = classes;
+    }
+
+    public static void setReason(String reason) {
+        REASON = reason;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void fail() {
+        StringBuffer tests = new StringBuffer();
+        for (Class c : CLASSES) {
+            if (tests.length() > 0) {
+                tests.append(", ");
+            }
+            tests.append(c.getName());
+        }
+        assert false : ("Failed to run test classes: " + tests.toString() + " because: " + REASON);
+    }
+}

Added: incubator/ace/trunk/test/src/net/luminis/test/osgi/dm/TestActivatorBase.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/test/src/net/luminis/test/osgi/dm/TestActivatorBase.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/test/src/net/luminis/test/osgi/dm/TestActivatorBase.java (added)
+++ incubator/ace/trunk/test/src/net/luminis/test/osgi/dm/TestActivatorBase.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,146 @@
+package net.luminis.test.osgi.dm;
+
+import static net.luminis.liq.test.utils.TestUtils.INTEGRATION;
+
+import java.util.List;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import net.luminis.liq.test.utils.TestUtils;
+
+import org.apache.felix.dependencymanager.DependencyActivatorBase;
+import org.apache.felix.dependencymanager.DependencyManager;
+import org.apache.felix.dependencymanager.Service;
+import org.apache.felix.dependencymanager.ServiceStateListener;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.testng.TestNG;
+
+/**
+ * Base class for integration tests within an OSGi framework.
+ */
+public abstract class TestActivatorBase extends DependencyActivatorBase implements ServiceStateListener, Runnable {
+    private BundleContext m_context;
+    private Semaphore m_semaphore;
+    @SuppressWarnings("unchecked")
+    private Class[] m_testClasses;
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public void init(BundleContext context, DependencyManager manager) throws Exception {
+        m_context = context;
+        m_testClasses = getTestClasses();
+        initServices(context, manager);
+        List services = manager.getServices();
+        m_semaphore = new Semaphore(1 - services.size());
+        for (Object s : services) {
+            Service service = (Service) s;
+            service.addStateListener(this);
+        }
+        Thread t = new Thread(this, "TestNG Runner");
+        t.start();
+    }
+
+    public void run() {
+        Thread.currentThread().setContextClassLoader(TestActivatorBase.class.getClassLoader());
+        TestNG testng = new TestNG();
+        // TODO come up with a good name here
+        testng.setDefaultSuiteName("Integration Test " + getClass().getName());
+
+        // ensure all bundles are started correctly
+        long timeout = System.currentTimeMillis() + 15000;
+        Bundle[] bundles = m_context.getBundles();
+        boolean allActive = true;
+        String nonActiveBundles = "";
+        do {
+            if (System.currentTimeMillis() > timeout) {
+                fail(testng, "Waiting for all bundles to become active timed out. Inactive bundles: " + nonActiveBundles);
+            }
+            StringBuffer nonActive = new StringBuffer("");
+            allActive = true;
+            for (Bundle b : bundles) {
+                if (b.getState() != Bundle.ACTIVE) {
+                    allActive = false;
+                    if (nonActive.length() > 0) {
+                        nonActive.append(", ");
+                    }
+                    nonActive.append(b.getSymbolicName());
+                }
+            }
+            nonActiveBundles = nonActive.toString();
+            if (!allActive) {
+                try { TimeUnit.MILLISECONDS.sleep(50); } catch (InterruptedException e) {}
+            }
+        }
+        while (allActive == false);
+
+        // wait for the service to be started
+        try {
+            if (m_semaphore.tryAcquire(30, TimeUnit.SECONDS)) {
+                // perform tests
+                testng.setTestClasses(m_testClasses);
+                testng.setGroups(INTEGRATION);
+                testng.setExcludedGroups(TestUtils.BROKEN);
+                testng.run();
+            }
+            else {
+                fail(testng, "Service(s) never started.");
+            }
+        }
+        catch (InterruptedException ie) {
+            fail(testng, "Thread was interrupted.");
+        }
+        // shutdown after the tests have been run
+        try {
+            m_context.getBundle(0).stop();
+            // Felix does not quit itself, which is a bug, so wait and exit manually
+            try { TimeUnit.MILLISECONDS.sleep(50); } catch (InterruptedException e) {}
+            try { System.exit(0); } catch (NoClassDefFoundError e) {}
+        }
+        catch (BundleException e) {
+            e.printStackTrace();
+            System.exit(20);
+        }
+    }
+
+    private void fail(TestNG testng, String reason) {
+        FailTests.setClasses(getTestClasses());
+        FailTests.setReason(reason);
+        testng.setTestClasses(new Class[] { FailTests.class });
+        testng.run();
+    }
+
+    /**
+     * Return the service object that specifies the test service and its
+     * dependencies.
+     */
+    protected abstract void initServices(BundleContext context, DependencyManager manager);
+
+    /**
+     * Return a list of test classes that are run.
+     */
+    @SuppressWarnings("unchecked")
+    protected abstract Class[] getTestClasses();
+
+    @Override
+    public void destroy(BundleContext context, DependencyManager manager) throws Exception {
+        for (Object s : manager.getServices()) {
+            Service service = (Service) s;
+            service.removeStateListener(this);
+        }
+    }
+
+    public void started(Service svc) {
+        m_semaphore.release();
+    }
+
+    public void starting(Service svc) {
+    }
+
+    public void stopped(Service svc) {
+    }
+
+    public void stopping(Service svc) {
+    }
+}

Added: incubator/ace/trunk/test/src/net/luminis/test/tools/BundleGenerator.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/test/src/net/luminis/test/tools/BundleGenerator.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/test/src/net/luminis/test/tools/BundleGenerator.java (added)
+++ incubator/ace/trunk/test/src/net/luminis/test/tools/BundleGenerator.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,75 @@
+package net.luminis.test.tools;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Random;
+import java.util.jar.JarEntry;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import org.osgi.framework.Constants;
+
+/**
+ * Tool that generates test bundles.
+ */
+public class BundleGenerator {
+    public static void main(String[] args) {
+
+        if (args.length != 5) {
+            System.out.println("Usage: java -jar BundleGenerator.jar [dir] [symbolicname] [version] [bundles] [size]");
+            System.exit(5);
+        }
+        try {
+            File destDir = new File(args[0]);
+            String symbolicName = args[1];
+            String version = args[2];
+            int bundles = Integer.parseInt(args[3]);
+            int size = Integer.parseInt(args[4]);
+
+            if (destDir.exists()) {
+                if (!destDir.isDirectory()) {
+                    throw new Exception("Destination " + destDir + " is not a directory.");
+                }
+            }
+            else {
+                if (!destDir.mkdirs()) {
+                    throw new Exception("Could not make directory: " + destDir);
+                }
+            }
+            System.out.println("Generating " + bundles + " bundles in " + destDir + " called " + symbolicName + " version " + version + " with " + size + " bytes of data.");
+
+            byte[] buffer = new byte[size];
+            Random random = new Random();
+            random.nextBytes(buffer);
+            for (int i = 0; i < bundles; i++) {
+                try {
+                    createBundle(destDir, symbolicName + i, version, buffer);
+                }
+                catch (Exception e) {
+                    System.err.println("Could not generate " + symbolicName + i + " version " + version + " because: " + e.getMessage());
+                }
+            }
+        }
+        catch (Exception e) {
+            System.err.println("Error: " + e.getMessage());
+            System.exit(20);
+        }
+
+    }
+
+    private static void createBundle(File destDir, String symbolicName, String version, byte[] data) throws FileNotFoundException, IOException {
+        File bundle = new File(destDir, symbolicName + "-" + version + ".jar");
+        Manifest manifest = new Manifest();
+        manifest.getMainAttributes().putValue("Manifest-Version", "1");
+        manifest.getMainAttributes().putValue(Constants.BUNDLE_MANIFESTVERSION, "2");
+        manifest.getMainAttributes().putValue(Constants.BUNDLE_SYMBOLICNAME, symbolicName);
+        manifest.getMainAttributes().putValue(Constants.BUNDLE_VERSION, version);
+        JarOutputStream jos = new JarOutputStream(new FileOutputStream(bundle), manifest);
+        JarEntry entry = new JarEntry("data");
+        jos.putNextEntry(entry);
+        jos.write(data);
+        jos.close();
+    }
+}

Added: incubator/ace/trunk/test/src/net/luminis/test/tools/RepositoryTool.java
URL: http://svn.apache.org/viewvc/incubator/ace/trunk/test/src/net/luminis/test/tools/RepositoryTool.java?rev=788992&view=auto
==============================================================================
--- incubator/ace/trunk/test/src/net/luminis/test/tools/RepositoryTool.java (added)
+++ incubator/ace/trunk/test/src/net/luminis/test/tools/RepositoryTool.java Sat Jun 27 15:53:04 2009
@@ -0,0 +1,200 @@
+package net.luminis.test.tools;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Properties;
+
+import javax.servlet.http.HttpServletResponse;
+
+import net.luminis.liq.repository.RangeIterator;
+import net.luminis.liq.repository.SortedRangeSet;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.Parser;
+import org.apache.commons.cli.PosixParser;
+
+/**
+ * Command line tool to access repositories.
+ */
+public class RepositoryTool {
+    private static final String MIME_APPLICATION_OCTET_STREAM = "application/octet-stream";
+    private static final int COPY_BUFFER_SIZE = 4096;
+
+    public static void main(String[] args) {
+        Parser parser = new PosixParser();
+        Options options = new Options();
+        options.addOption("h", "host", true, "Host URL of the repository");
+        options.addOption("c", "command", true, "Command to send (query, commit, checkout, get, put, backup, restore)");
+        options.addOption("C", "customer", true, "Customer ID");
+        options.addOption("n", "name", true, "Repository name");
+        options.addOption("v", "version", true, "Version");
+        options.addOption("f", "file", true, "File or directory");
+
+        Properties props = new Properties();
+        try {
+            props.load(new FileInputStream(new File(".repository")));
+        }
+        catch (FileNotFoundException e) {
+            // if the file does not exist, simply ignore it
+        }
+        catch (IOException e) {
+            System.err.println("I/O exception while reading defaults in .repository: " + e);
+        }
+        try {
+            CommandLine line = parser.parse(options, args, props);
+            String host = line.getOptionValue("host", "http://localhost:8080/");
+            String cmd = line.getOptionValue("command", "query");
+            String customer = line.getOptionValue("customer");
+            String name = line.getOptionValue("name");
+            String version = line.getOptionValue("version", "0");
+            String file = line.getOptionValue("file", "file");
+            try {
+                if ("query".equals(cmd)) {
+                    query(host, customer, name);
+                }
+                else if ("commit".equals(cmd)) {
+                    put("repository/commit", host, customer, name, version, file);
+                }
+                else if ("checkout".equals(cmd)) {
+                    get("repository/checkout", host, customer, name, version, file);
+                }
+                else if ("put".equals(cmd)) {
+                    put("replication/put", host, customer, name, version, file);
+                }
+                else if ("get".equals(cmd)) {
+                    get("replication/get", host, customer, name, version, file);
+                }
+                else if ("backup".equals(cmd)) {
+                    backup(host, customer, name, file);
+
+                }
+                else if ("restore".equals(cmd)) {
+                    restore(host, customer, name, file);
+                }
+                else {
+                    showHelp(options);
+                }
+            }
+            catch (IOException e) {
+                System.err.println("Could not connect to host URL: " + e);
+            }
+            catch (Exception e) {
+                System.err.println("Error: " + e);
+            }
+        }
+        catch (ParseException exp) {
+            System.err.println("Unexpected exception:" + exp.getMessage());
+            showHelp(options);
+        }
+    }
+
+    private static void restore(String host, String customer, String name, String file) throws MalformedURLException, IOException, FileNotFoundException {
+        File dir = new File(file);
+        if (!dir.isDirectory()) {
+            System.err.println("Backup directory does not exist: " + file);
+            System.exit(5);
+        }
+        File[] versions = dir.listFiles();
+        if (versions != null) {
+            for (int i = 0; i < versions.length; i++) {
+                put("replication/put", host, customer, name, versions[i].getName(), versions[i].getAbsolutePath());
+            }
+        }
+    }
+
+    private static void backup(String host, String customer, String name, String file) throws MalformedURLException, IOException {
+        File dir = new File(file);
+        if (!dir.isDirectory()) {
+            if (!dir.mkdirs()) {
+                System.err.println("Could not make backup directory " + file);
+                System.exit(5);
+            }
+        }
+        URL query = new URL(new URL(host), "replication/query" + "?customer=" + customer + "&name=" + name);
+        HttpURLConnection connection = (HttpURLConnection) query.openConnection();
+        if (connection.getResponseCode() == HttpServletResponse.SC_OK) {
+            BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
+            try {
+                String l = reader.readLine();
+                int i = l.lastIndexOf(',');
+                if (i > 0) {
+                    SortedRangeSet remoteRange = new SortedRangeSet(l.substring(i + 1));
+                    RangeIterator iterator = remoteRange.iterator();
+                    while (iterator.hasNext()) {
+                        String v = Long.toString(iterator.next());
+                        String f = (new File(dir, v)).getAbsolutePath();
+                        get("replication/get", host, customer, name, v, f);
+                    }
+                }
+            }
+            catch (Exception e) {
+                System.err.println("Error parsing remote range " + e);
+                System.exit(5);
+            }
+        }
+        else {
+            System.err.println("Could not make backup for customer " + customer + " name " + name);
+            System.exit(5);
+        }
+    }
+
+    private static void query(String host, String customer, String name) throws MalformedURLException, IOException {
+        String f1 = (customer == null) ? null : "customer=" + customer;
+        String f2 = (name == null) ? null : "name=" + name;
+        String filter = ((f1 == null) ? "?" : "?" + f1 + "&") + ((f2 == null) ? "" : f2);
+        URL url = new URL(new URL(host), "repository/query" + filter);
+        URLConnection connection = url.openConnection();
+        InputStream input = connection.getInputStream();
+        copy(input, System.out);
+    }
+
+    private static void get(String endpoint, String host, String customer, String name, String version, String file) throws MalformedURLException, IOException, FileNotFoundException {
+        URL url = new URL(new URL(host), endpoint + "?customer=" + customer + "&name=" + name + "&version=" + version);
+        URLConnection connection = url.openConnection();
+        InputStream input = connection.getInputStream();
+        OutputStream out = new FileOutputStream(file);
+        copy(input, out);
+        out.flush();
+    }
+
+    private static void put(String endpoint, String host, String customer, String name, String version, String file) throws MalformedURLException, IOException, FileNotFoundException {
+        URL url = new URL(new URL(host), endpoint + "?customer=" + customer + "&name=" + name + "&version=" + version);
+        URLConnection connection = url.openConnection();
+        connection.setDoOutput(true);
+        connection.setRequestProperty("Content-Type", MIME_APPLICATION_OCTET_STREAM);
+        OutputStream out = connection.getOutputStream();
+        InputStream input = new FileInputStream(file);
+        copy(input, out);
+        out.flush();
+        InputStream is = (InputStream) connection.getContent();
+        is.close();
+    }
+
+    private static void showHelp(Options options) {
+        HelpFormatter formatter = new HelpFormatter();
+        formatter.printHelp("RepositoryTool", options);
+    }
+
+    private static void copy(InputStream in, OutputStream out) throws IOException {
+        byte[] buffer = new byte[COPY_BUFFER_SIZE];
+        int bytes = in.read(buffer);
+        while (bytes != -1) {
+            out.write(buffer, 0, bytes);
+            bytes = in.read(buffer);
+        }
+    }
+}