Move ServiceMix Kernel trunk into Felix

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@768912 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/karaf/testing/itests/pom.xml b/karaf/testing/itests/pom.xml
new file mode 100644
index 0000000..8442902
--- /dev/null
+++ b/karaf/testing/itests/pom.xml
@@ -0,0 +1,77 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <!--
+
+        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.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel.testing</groupId>
+        <artifactId>testing</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.testing</groupId>
+    <artifactId>org.apache.servicemix.kernel.testing.itests</artifactId>
+    <packaging>jar</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: Testing - ITests</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel.testing</groupId>
+            <artifactId>org.apache.servicemix.kernel.testing.support</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.specs</groupId>
+            <artifactId>org.apache.servicemix.specs.jaxp-api-1.4</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.woodstox</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.jaxp-ri</artifactId>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- generate dependencies versions -->
+            <plugin>
+                <groupId>org.apache.servicemix.tooling</groupId>
+                <artifactId>depends-maven-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>generate-depends-file</id>
+                        <goals>
+                            <goal>generate-depends-file</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
\ No newline at end of file
diff --git a/karaf/testing/itests/src/test/java/org/apache/servicemix/kernel/testing/itests/SimpleTest.java b/karaf/testing/itests/src/test/java/org/apache/servicemix/kernel/testing/itests/SimpleTest.java
new file mode 100644
index 0000000..dec38ae
--- /dev/null
+++ b/karaf/testing/itests/src/test/java/org/apache/servicemix/kernel/testing/itests/SimpleTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.servicemix.kernel.testing.itests;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.stream.XMLInputFactory;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.validation.SchemaFactory;
+
+import org.apache.servicemix.kernel.testing.support.AbstractIntegrationTest;
+import org.osgi.framework.Bundle;
+
+public class SimpleTest extends AbstractIntegrationTest {
+
+    static {
+        System.setProperty("jaxp.debug", "true");
+        System.setProperty("org.apache.servicemix.specs.debug", "true");
+    }
+
+    /**
+	 * The manifest to use for the "virtual bundle" created
+	 * out of the test classes and resources in this project
+	 *
+	 * This is actually the boilerplate manifest with one additional
+	 * import-package added. We should provide a simpler customization
+	 * point for such use cases that doesn't require duplication
+	 * of the entire manifest...
+	 */
+	protected String getManifestLocation() {                            
+		return "classpath:org/apache/servicemix/MANIFEST.MF";
+	}
+
+	/**
+	 * The location of the packaged OSGi bundles to be installed
+	 * for this test. Values are Spring resource paths. The bundles
+	 * we want to use are part of the same multi-project maven
+	 * build as this project is. Hence we use the localMavenArtifact
+	 * helper method to find the bundles produced by the package
+	 * phase of the maven build (these tests will run after the
+	 * packaging phase, in the integration-test phase).
+	 *
+	 * JUnit, commons-logging, spring-core and the spring OSGi
+	 * test bundle are automatically included so do not need
+	 * to be specified here.
+	 */
+	protected String[] getTestBundlesNames() {
+        return new String[0];
+	}
+
+    /**
+     * Do not include the jaxp-ri bundle by default, as we want to test it
+     * @return
+     */
+    @Override
+    protected String[] getTestFrameworkBundlesNames() {
+        return new String[] {
+            getBundle("org.apache.geronimo.specs", "geronimo-servlet_2.5_spec"),
+            getBundle("org.apache.servicemix.specs", "org.apache.servicemix.specs.jaxp-api-1.4"),
+            getBundle("org.apache.felix", "org.osgi.compendium"),
+            getBundle("org.apache.felix", "org.apache.felix.configadmin"),
+            getBundle("org.ops4j.pax.logging", "pax-logging-api"),
+            getBundle("org.ops4j.pax.logging", "pax-logging-service"),
+            getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.aopalliance"),
+            getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.asm"),
+            getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.junit"),
+            getBundle("org.springframework", "spring-beans"),
+            getBundle("org.springframework", "spring-core"),
+            getBundle("org.springframework", "spring-context"),
+            getBundle("org.springframework", "spring-aop"),
+            getBundle("org.springframework", "spring-test"),
+            getBundle("org.springframework.osgi", "spring-osgi-core"),
+            getBundle("org.springframework.osgi", "spring-osgi-io"),
+            getBundle("org.springframework.osgi", "spring-osgi-extender"),
+            getBundle("org.springframework.osgi", "spring-osgi-test"),
+            getBundle("org.springframework.osgi", "spring-osgi-annotation"),
+            getBundle("org.apache.servicemix.kernel.testing", "org.apache.servicemix.kernel.testing.support"),
+		};
+    }
+
+    public void testDocumentBuilderFactory() throws Exception {
+        try {
+            DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
+            fail("Implementation should not have been found");
+        } catch (Throwable t) {
+        }
+        Bundle b = installBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.jaxp-ri", null, "jar");
+        try {
+		    assertNotNull(DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument());
+        } finally {
+            b.uninstall();
+        }
+    }
+
+    public void testTransformerFactory() throws Exception {
+        try {
+            TransformerFactory.newInstance().newTransformer();
+            fail("Implementation should not have been found");
+        } catch (Throwable t) {
+        }
+        Bundle b = installBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.jaxp-ri", null, "jar");
+        try {
+            assertNotNull(TransformerFactory.newInstance().newTransformer());
+        } finally {
+            b.uninstall();
+        }
+    }
+
+    public void testSchemaFactory() throws Exception {
+        try {
+            SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema").newSchema();
+            fail("Implementation should not have been found");
+        } catch (Throwable t) {
+        }
+        Bundle b = installBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.jaxp-ri", null, "jar");
+        try {
+            assertNotNull(SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema").newSchema());
+        } finally {
+            b.uninstall();
+        }
+    }
+
+    public void testStax() throws Exception {
+        try {
+            XMLInputFactory.newInstance();
+            fail("Implementation should not have been found");
+        } catch (Throwable t) {
+        }
+        Bundle b = installBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.jaxp-ri", null, "jar");
+        try {
+            assertNotNull(XMLInputFactory.newInstance());
+        } finally {
+            b.uninstall();
+        }
+    }
+
+}
diff --git a/karaf/testing/itests/src/test/resources/log4j.properties b/karaf/testing/itests/src/test/resources/log4j.properties
new file mode 100644
index 0000000..7944dfb
--- /dev/null
+++ b/karaf/testing/itests/src/test/resources/log4j.properties
@@ -0,0 +1,43 @@
+# 
+# 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.
+#
+#
+
+#
+# The logging properties used during tests..
+#
+log4j.rootLogger=DEBUG, stdout
+
+log4j.logger.org.springframework=INFO
+log4j.logger.org.apache.activemq=INFO
+log4j.logger.org.apache.activemq.spring=WARN
+
+
+#log4j.logger.org.apache.servicemix=DEBUG
+
+# CONSOLE appender 
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+
+# File appender
+log4j.appender.out=org.apache.log4j.FileAppender
+log4j.appender.out.layout=org.apache.log4j.PatternLayout
+log4j.appender.out.layout.ConversionPattern=%d [%-15.15t] %-5p %-30.30c{1} - %m%n
+log4j.appender.out.file=target/servicemix-test.log
+log4j.appender.out.append=true
diff --git a/karaf/testing/itests/src/test/resources/org/apache/servicemix/MANIFEST.MF b/karaf/testing/itests/src/test/resources/org/apache/servicemix/MANIFEST.MF
new file mode 100644
index 0000000..f26d98a
--- /dev/null
+++ b/karaf/testing/itests/src/test/resources/org/apache/servicemix/MANIFEST.MF
@@ -0,0 +1,32 @@
+Manifest-Version: 1.0
+License-00: .
+License-01: Licensed to the Apache Software Foundation (ASF) under one or more
+License-02: contributor license agreements.  See the NOTICE file distributed with
+License-03: this work for additional information regarding copyright ownership.
+License-04: The ASF licenses this file to You under the Apache License, Version 2.0
+License-05: (the "License"); you may not use this file except in compliance with
+License-06: the License.  You may obtain a copy of the License at
+License-07: .
+License-08:      http://www.apache.org/licenses/LICENSE-2.0
+License-09: .
+License-10: Unless required by applicable law or agreed to in writing, software
+License-11: distributed under the License is distributed on an "AS IS" BASIS,
+License-12: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+License-13: See the License for the specific language governing permissions and
+License-14: limitations under the License.
+License-15: .
+Bundle-Name: org.apache.servicemix.testing.itests
+Bundle-SymbolicName: org.apache.servicemix.testing.itests
+Bundle-Vendor: Spring Framework
+Bundle-Activator: org.springframework.osgi.test.JUnitTestActivator
+Import-Package: junit.framework,
+ org.osgi.framework;specification-version="1.3.0",
+ org.apache.commons.logging,
+ org.springframework.core.io,
+ org.springframework.osgi.test,
+ org.apache.servicemix.kernel.testing.support,
+ org.springframework.osgi.util,
+ javax.xml.stream,
+ javax.xml.transform,
+ javax.xml.validation,
+ javax.xml.parsers
diff --git a/karaf/testing/pom.xml b/karaf/testing/pom.xml
new file mode 100644
index 0000000..9a62891
--- /dev/null
+++ b/karaf/testing/pom.xml
@@ -0,0 +1,41 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <!--
+
+        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.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel</groupId>
+        <artifactId>kernel</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.testing</groupId>
+    <artifactId>testing</artifactId>
+    <packaging>pom</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: Testing</name>
+
+    <modules>
+        <module>support</module>
+        <module>itests</module>
+    </modules>
+
+</project>
diff --git a/karaf/testing/support/pom.xml b/karaf/testing/support/pom.xml
new file mode 100644
index 0000000..7eb620a
--- /dev/null
+++ b/karaf/testing/support/pom.xml
@@ -0,0 +1,149 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <!--
+
+        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.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.servicemix.kernel.testing</groupId>
+        <artifactId>testing</artifactId>
+        <version>1.2.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.apache.servicemix.kernel.testing</groupId>
+    <artifactId>org.apache.servicemix.kernel.testing.support</artifactId>
+    <packaging>bundle</packaging>
+    <version>1.2.0-SNAPSHOT</version>
+    <name>Apache ServiceMix Kernel :: Testing - Support</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.servicemix.kernel</groupId>
+            <artifactId>org.apache.servicemix.kernel.main</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.configadmin</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.logging</groupId>
+            <artifactId>pax-logging-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.logging</groupId>
+            <artifactId>pax-logging-service</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.osgi</groupId>
+            <artifactId>spring-osgi-test</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.osgi</groupId>
+            <artifactId>spring-osgi-extender</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.osgi</groupId>
+            <artifactId>spring-osgi-annotation</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-servlet_2.5_spec</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.specs</groupId>
+            <artifactId>org.apache.servicemix.specs.jaxp-api-1.4</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.aopalliance</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.asm</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.woodstox</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.servicemix.bundles</groupId>
+            <artifactId>org.apache.servicemix.bundles.jaxp-ri</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.ops4j.pax.url</groupId>
+            <artifactId>pax-url-mvn</artifactId>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-resources-plugin</artifactId>
+                <executions>
+                    <execution>
+                    <id>copy-resources</id>
+                    <!-- here the phase you need -->
+                    <phase>compile</phase>
+                    <goals>
+                        <goal>copy-resources</goal>
+                    </goals>
+                    <configuration>
+                            <outputDirectory>${basedir}/target/classes/org/apache/servicemix/kernel/testing/support/</outputDirectory>
+                            <resources>          
+                                <resource>
+                                    <directory>../../${config.location}</directory>
+                                        <includes>
+                                            <include>config.properties</include>
+                                        </includes>
+                                </resource>
+                            </resources>              
+                        </configuration>            
+                    </execution>
+                </executions>
+            </plugin>            
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+                        <Import-Package>
+                            org.apache.felix*;resolution:=optional,
+                            org.apache.servicemix.kernel.main;resolution:=optional,
+                            org.apache.servicemix.kernel.main.spi;resolution:=optional;version="1.0.0",
+                            *
+                        </Import-Package>
+                        <Export-Package>${pom.artifactId}</Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/AbstractIntegrationTest.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/AbstractIntegrationTest.java
new file mode 100644
index 0000000..14cc73a
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/AbstractIntegrationTest.java
@@ -0,0 +1,268 @@
+/*
+ * 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.servicemix.kernel.testing.support;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import org.apache.log4j.PropertyConfigurator;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.springframework.core.io.FileSystemResource;
+import org.springframework.core.io.Resource;
+import org.springframework.osgi.test.AbstractConfigurableBundleCreatorTests;
+import org.springframework.osgi.test.provisioning.ArtifactLocator;
+import org.springframework.osgi.util.OsgiFilterUtils;
+import org.springframework.osgi.util.OsgiListenerUtils;
+import org.springframework.util.Assert;
+import org.springframework.util.StringUtils;
+
+
+public class AbstractIntegrationTest extends AbstractConfigurableBundleCreatorTests {
+
+    static {
+        try {
+            File f = new File("target/smx4");
+            f.mkdirs();
+            System.setProperty("servicemix.name", "root");
+            System.setProperty("servicemix.home", f.getAbsolutePath());
+            System.setProperty("servicemix.base", f.getAbsolutePath());
+            System.setProperty("org.apache.servicemix.filemonitor.configDir", new File(f, "etc").getAbsolutePath());
+            System.setProperty("org.apache.servicemix.filemonitor.monitorDir", new File(f, "deploy").getAbsolutePath());
+            System.setProperty("org.apache.servicemix.filemonitor.generatedJarDir", new File(f, "data/generate-bundles").getAbsolutePath());
+            System.setProperty("bundles.configuration.location", new File("src/test/conf").getAbsolutePath());
+            System.setProperty("org.osgi.vendor.framework", "org.apache.servicemix.kernel.testing.support");
+            PropertyConfigurator.configure("target/test-classes/log4j.properties");
+        } catch (Throwable t) {}
+    }
+
+    private Properties dependencies;
+    private FeatureInstaller featureInstaller;
+
+    @Override
+    protected String getPlatformName() {
+        String systemProperty = System.getProperty(OSGI_FRAMEWORK_SELECTOR);
+        if (logger.isTraceEnabled())
+            logger.trace("system property [" + OSGI_FRAMEWORK_SELECTOR + "] has value=" + systemProperty);
+
+        return (systemProperty == null ? SmxKernelPlatform.class.getName() : systemProperty);
+    }
+
+    protected String getBundle(String groupId, String artifactId) {
+        return groupId + "," + artifactId + "," + getBundleVersion(groupId, artifactId);
+    }
+
+    protected String getBundleVersion(String groupId, String artifactId) {
+        if (dependencies == null) {
+            try {
+                File f = new File(System.getProperty("basedir"), "target/classes/META-INF/maven/dependencies.properties");
+                Properties prop = new Properties();
+                prop.load(new FileInputStream(f));
+                dependencies = prop;
+            } catch (IOException e) {
+                throw new IllegalStateException("Unable to load dependencies informations", e);
+            }
+        }
+        String version = dependencies.getProperty(groupId + "/" + artifactId + "/version");
+        if (version == null) {
+            throw new IllegalStateException("Unable to find dependency information for: " + groupId + "/" + artifactId + "/version");
+        }
+        return version;
+    }
+
+    protected String[] getTestFrameworkBundlesNames() {
+        return new String[] {
+            getBundle("org.apache.geronimo.specs", "geronimo-servlet_2.5_spec"),
+            getBundle("org.apache.servicemix.specs", "org.apache.servicemix.specs.jaxp-api-1.4"),
+            getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.jaxp-ri"),
+            getBundle("org.apache.felix", "org.osgi.compendium"),
+            getBundle("org.apache.felix", "org.apache.felix.configadmin"),
+            getBundle("org.ops4j.pax.logging", "pax-logging-api"),
+            getBundle("org.ops4j.pax.logging", "pax-logging-service"),
+            getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.aopalliance"),
+            getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.asm"),
+            getBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.junit"),
+            getBundle("org.springframework", "spring-beans"),
+            getBundle("org.springframework", "spring-core"),
+            getBundle("org.springframework", "spring-context"),
+            getBundle("org.springframework", "spring-aop"),
+            getBundle("org.springframework", "spring-test"),
+            getBundle("org.springframework.osgi", "spring-osgi-core"),
+            getBundle("org.springframework.osgi", "spring-osgi-io"),
+            getBundle("org.springframework.osgi", "spring-osgi-extender"),
+            getBundle("org.springframework.osgi", "spring-osgi-test"),
+            getBundle("org.springframework.osgi", "spring-osgi-annotation"),
+            getBundle("org.ops4j.pax.url", "pax-url-mvn"),
+            getBundle("org.apache.servicemix.kernel.testing", "org.apache.servicemix.kernel.testing.support"),
+        };
+    }
+
+    protected Bundle installBundle(String groupId, String artifactId, String classifier, String type) throws Exception {
+        String version = getBundleVersion(groupId, artifactId);
+        File loc = localMavenBundle(groupId, artifactId, version, classifier, type);
+        Bundle bundle = bundleContext.installBundle(loc.toURI().toString());
+        bundle.start();
+        return bundle;
+    }
+    
+    protected void addFeatureRepo(String url) throws Exception {
+        if (featureInstaller == null) {
+            featureInstaller = new FeatureInstaller();
+            featureInstaller.setBundleContext(bundleContext);
+        }
+        featureInstaller.addRepository(new URI(url));
+    }
+    
+    protected void installFeature(String name) throws Exception {
+        installFeature(name, FeatureImpl.DEFAULT_VERSION);
+    }
+
+    protected void installFeature(String name, String version) throws Exception {
+        if (featureInstaller == null) {
+            featureInstaller = new FeatureInstaller();
+            featureInstaller.setBundleContext(bundleContext);
+            
+        }
+        featureInstaller.installFeature(name, version);
+    }
+    
+    protected Resource locateBundle(String bundleId) {
+        Assert.hasText(bundleId, "bundleId should not be empty");
+
+        // parse the String
+        String[] artifactId = StringUtils.commaDelimitedListToStringArray(bundleId);
+
+        Assert.isTrue(artifactId.length >= 3, "the CSV string " + bundleId + " contains too few values");
+        // TODO: add a smarter mechanism which can handle 1 or 2 values CSVs
+        for (int i = 0; i < artifactId.length; i++) {
+            artifactId[i] = StringUtils.trimWhitespace(artifactId[i]);
+        }
+
+        File f;
+        if (artifactId.length == 3) {
+            f = localMavenBundle(artifactId[0], artifactId[1], artifactId[2], null, ArtifactLocator.DEFAULT_ARTIFACT_TYPE);
+        } else {
+            f = localMavenBundle(artifactId[0], artifactId[1], artifactId[2], null, artifactId[3]);
+        }
+        return new FileSystemResource(f);
+    }
+
+
+    protected File localMavenBundle(String groupId, String artifact, String version, String classifier, String type) {
+        String defaultHome = new File(new File(System.getProperty("user.home")), ".m2/repository").getAbsolutePath();
+        File repositoryHome = new File(System.getProperty("localRepository", defaultHome));
+
+        StringBuffer location = new StringBuffer(groupId.replace('.', '/'));
+        location.append('/');
+        location.append(artifact);
+        location.append('/');
+        location.append(getSnapshot(version));
+        location.append('/');
+        location.append(artifact);
+        location.append('-');
+        location.append(version);
+        if (classifier != null) {
+            location.append('-');
+            location.append(classifier);
+        }
+        location.append(".");
+        location.append(type);
+
+        return new File(repositoryHome, location.toString());
+    }
+
+    protected static String getSnapshot(String version) {
+        if (isTimestamped(version)) {
+            return version.substring(0, version.lastIndexOf('-', version.lastIndexOf('-') - 1)) + "-SNAPSHOT";
+        }
+        return version;
+    }
+
+    protected static boolean isTimestamped(String version) {
+        return version.matches(".+-\\d\\d\\d\\d\\d\\d\\d\\d\\.\\d\\d\\d\\d\\d\\d-\\d+");
+    }
+
+    protected static boolean isSnapshot(String version) {
+        return version.matches(".+-SNAPSHOT");
+    }
+
+    public <T> T getOsgiService(Class<T> type) {
+        return getOsgiService(type, DEFAULT_WAIT_TIME);
+    }
+
+    public <T> T getOsgiService(Class<T> type, long timeout) {
+        // translate from seconds to miliseconds
+        long time = timeout * 1000;
+
+        // use the counter to make sure the threads block
+        final Counter counter = new Counter("waitForOsgiService on bnd=" + type.getName());
+
+        counter.increment();
+
+        final List<T> services = new ArrayList<T>();
+
+        ServiceListener listener = new ServiceListener() {
+            public void serviceChanged(ServiceEvent event) {
+                if (event.getType() == ServiceEvent.REGISTERED) {
+                    services.add((T) bundleContext.getService(event.getServiceReference()));
+                    counter.decrement();
+                }
+            }
+        };
+
+        String filter = OsgiFilterUtils.unifyFilter(type.getName(), null);
+        OsgiListenerUtils.addServiceListener(bundleContext, listener, filter);
+
+        if (logger.isDebugEnabled())
+            logger.debug("start waiting for OSGi service=" + type.getName());
+
+        try {
+            if (counter.waitForZero(time)) {
+                logger.warn("waiting for OSGi service=" + type.getName() + " timed out");
+                throw new RuntimeException("Gave up waiting for OSGi service '" + type.getName() + "' to be created");
+            }
+            else if (logger.isDebugEnabled()) {
+                logger.debug("found OSGi service=" + type.getName());
+            }
+            return services.get(0);
+        }
+        finally {
+            // inform waiting thread
+            bundleContext.removeServiceListener(listener);
+        }
+    }
+
+    protected void checkBundleStarted(String name) {
+        assertNotNull(bundleContext);
+        for (int i = 0; i < bundleContext.getBundles().length; i++) {
+            Bundle b = bundleContext.getBundles()[i];
+            if (b.getSymbolicName().equals(name)) {
+                assertEquals("Bundle '" + name + "' is not active", Bundle.ACTIVE, b.getState());
+                return;
+            }
+        }
+        fail("Bundle '" + name + "' not found");
+    }
+
+
+}
\ No newline at end of file
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/Counter.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/Counter.java
new file mode 100644
index 0000000..95a3a44
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/Counter.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2002-2007 the original author or authors.
+ *
+ * Licensed 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.servicemix.kernel.testing.support;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Simple counting class which can be incremented or decremented in a
+ * synchronized manner. This class can be used as a synchronization mechanism
+ * between threads mainly though {@link #waitForZero(long)} method.
+ *
+ * The main usage of the class is to allow a master thread, to know when other
+ * threads (slaves) have passed a certain point in execution.
+ *
+ * <p/> As opposed to a Barrier or a Semaphore, this class should be used only
+ * with 1 waiting thread (a master) and any number of slave threads.
+ *
+ * <pre style="code">
+ * Thread 1:
+ *  synchronized(counter) {
+ *    counter.increment();
+ *    thread2.start();
+ *    counter.increment();
+ *    thread3.start();
+ *
+ *    // wait 1 second for other threads to complete
+ *    counter.waitForZero(1000);
+ * }
+ *
+ * Thread 2:
+ *  // do some work
+ *  counter.decrement();
+ *
+ * Thread 3:
+ *  // do some work
+ *  counter.decrement();
+ *
+ * </pre>
+ *
+ * <p/> Mainly for usage inside the framework. All methods are thread-safe
+ * however for the master/slave pattern, synchronized blocks are recommended as
+ * multiple operations have to be executed at once.
+ *
+ * @author Costin Leau
+ *
+ */
+public class Counter {
+
+	private int counter = 0;
+
+	private static final Log log = LogFactory.getLog(Counter.class);
+
+	private final String name;
+
+	/**
+	 * Create counter with a given name.
+	 * @param name counter name
+	 */
+	public Counter(String name) {
+		this.name = name;
+	}
+
+	/**
+	 * Increment the counter value.
+	 */
+	public synchronized void increment() {
+		counter++;
+		if (log.isTraceEnabled())
+			log.trace("counter [" + name + "] incremented to " + counter);
+	}
+
+	/**
+	 * Decrement the counter value.
+	 */
+	public synchronized void decrement() {
+		counter--;
+		if (log.isTraceEnabled())
+			log.trace("counter [" + name + "] decremented to " + counter);
+		notifyAll();
+	}
+
+	public synchronized boolean decrementAndWait(long timeToWait) {
+		decrement();
+		if (counter > 0)
+			return waitForZero(timeToWait);
+		return true;
+	}
+
+	/**
+	 * Check if the counter value is zero.
+	 * @return true if value is equal or below zero, false otherwise.
+	 */
+	public synchronized boolean isZero() {
+		return is(0);
+	}
+
+	public synchronized boolean is(int value) {
+		return counter == value;
+	}
+
+	/**
+	 * Return the counter value.
+	 *
+	 * @return the counter value.
+	 */
+	public synchronized int getValue() {
+		return counter;
+	}
+
+	public synchronized String toString() {
+		return "" + counter;
+	}
+
+	/**
+	 * Specialized method which waits for 0. Identical to waitFor(0, waitTime).
+	 *
+	 * @see #waitFor(int, long)
+	 * @param waitTime
+	 * @return true if the waiting timed out, false otherwise
+	 */
+	public synchronized boolean waitForZero(long waitTime) {
+		return waitFor(0, waitTime);
+	}
+
+	/**
+	 * Wait maximum the givem amount of time, for the counter to reach the given
+	 * value.. This mechanism relies on {@link Object#wait(long)} and
+	 * {@link Object#notify()} mechanism to work appropriately. Please see the
+	 * class javadoc for more info.
+	 *
+	 * <p/> This method will stop waiting and return true if the thread
+	 * is interrupted.
+	 *
+	 * @param value the value to wait for
+	 * @param waitTime the time (in miliseconds) to wait for zero value
+	 * @return true if the waiting timed out, false otherwise
+	 */
+	public synchronized boolean waitFor(int value, long waitTime) {
+		boolean timedout = false;
+		long remainingTime = waitTime;
+		long startTime = System.currentTimeMillis();
+
+		while (counter > value && !timedout) {
+			// start waiting
+			try {
+				this.wait(remainingTime);
+				// compute the remaining time
+				remainingTime = waitTime - (System.currentTimeMillis() - startTime);
+				timedout = remainingTime <= 0;
+			}
+			catch (InterruptedException ex) {
+				timedout = true;
+			}
+		}
+
+		return timedout;
+	}
+}
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/Feature.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/Feature.java
new file mode 100644
index 0000000..56006cf
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/Feature.java
@@ -0,0 +1,45 @@
+/*
+ * 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.servicemix.kernel.testing.support;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A feature is a list of bundles associated identified by its name.
+ */
+public interface Feature {
+
+    String getId();
+
+    String getName();
+
+    String getVersion();
+
+    List<Feature> getDependencies();
+
+    List<String> getBundles();
+
+    Map<String, Map<String, String>> getConfigurations();
+    
+    void addDependency(Feature dependency);
+    
+    void addBundle(String bundle);
+    
+    void addConfig(String name, Map<String,String> properties);
+
+}
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureImpl.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureImpl.java
new file mode 100644
index 0000000..9c8c9a2
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureImpl.java
@@ -0,0 +1,125 @@
+/*
+ * 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.servicemix.kernel.testing.support;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+
+public class FeatureImpl implements Feature {
+
+    private String id;
+    private String name;
+    private String version;
+    private List<Feature> dependencies = new ArrayList<Feature>();
+    private List<String> bundles = new ArrayList<String>();
+    private Map<String, Map<String,String>> configs = new HashMap<String, Map<String,String>>();
+    public static String SPLIT_FOR_NAME_AND_VERSION = "_split_for_name_and_version_";
+    public static String DEFAULT_VERSION = "0.0.0";
+    
+    public FeatureImpl(String name) {
+        this(name, DEFAULT_VERSION);
+    }
+    
+    public FeatureImpl(String name, String version) {
+        this.name = name;
+        this.version = version;
+        this.id = name + "-" + version;
+    }
+
+    
+    public String getId() {
+        return id;
+    }
+
+    
+    public String getName() {
+        return name;
+    }
+
+    
+    public String getVersion() {
+        return version;
+    }
+    
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    
+    public List<Feature> getDependencies() {
+        return dependencies;
+    }
+
+    public List<String> getBundles() {
+        return bundles;
+    }
+
+    public Map<String, Map<String, String>> getConfigurations() {
+        return configs;
+    }
+
+    public void addDependency(Feature dependency) {
+        dependencies.add(dependency);
+    }
+
+    public void addBundle(String bundle) {
+        bundles.add(bundle);
+    }
+
+    public void addConfig(String name, Map<String,String> properties) {
+        configs.put(name, properties);
+    }
+
+    public String toString() {
+        String ret = getName() + SPLIT_FOR_NAME_AND_VERSION + getVersion();
+        return ret;
+    }
+    
+    public static Feature valueOf(String str) {
+        if (str.indexOf(SPLIT_FOR_NAME_AND_VERSION) >= 0) {
+            String strName = str.substring(0, str.indexOf(SPLIT_FOR_NAME_AND_VERSION));
+            String strVersion = str.substring(str.indexOf(SPLIT_FOR_NAME_AND_VERSION) 
+                    + SPLIT_FOR_NAME_AND_VERSION.length(), str.length());
+            return new FeatureImpl(strName, strVersion);
+        } else {
+            return new FeatureImpl(str);
+        }
+                
+        
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        FeatureImpl feature = (FeatureImpl) o;
+
+        if (!name.equals(feature.name)) return false;
+        if (!version.equals(feature.version)) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result = name.hashCode();
+        result = 31 * result + version.hashCode();
+        return result;
+    }
+}
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureInstaller.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureInstaller.java
new file mode 100644
index 0000000..d200e40
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureInstaller.java
@@ -0,0 +1,246 @@
+/*
+ * 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.servicemix.kernel.testing.support;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URL;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+
+
+public class FeatureInstaller {
+
+    private Map<URI, FeatureRepositoryImpl> repositories = new HashMap<URI, FeatureRepositoryImpl>();
+    private Map<String, Map<String, Feature>> features;
+    private BundleContext bundleContext;
+    
+    public void addRepository(URI uri) throws Exception {
+        if (!repositories.values().contains(uri)) {
+            internalAddRepository(uri);
+        }
+    }
+    
+    protected FeatureRepositoryImpl internalAddRepository(URI uri) throws Exception {
+        FeatureRepositoryImpl repo = new FeatureRepositoryImpl(uri);
+        repositories.put(uri, repo);
+        features = null;
+        return repo;
+    }
+    
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+    
+    
+    public void installFeature(String name, String version) throws Exception {
+        Feature f = getFeature(name, version);
+        if (f == null) {
+            throw new Exception("No feature named '" + name 
+                    + "' with version '" + version + "' available");
+        }
+        for (Feature dependency : f.getDependencies()) {
+            installFeature(dependency.getName(), dependency.getVersion());
+        }
+       
+        Set<Long> bundles = new HashSet<Long>();
+        for (String bundleLocation : f.getBundles()) {
+            Bundle b = installBundleIfNeeded(bundleLocation);
+            bundles.add(b.getBundleId());
+        }
+        for (long id : bundles) {
+            bundleContext.getBundle(id).start();
+        }
+
+        
+    }
+    
+    protected Feature getFeature(String name, String version) throws Exception {
+        Map<String, Feature> versions = getFeatures().get(name);
+        if (versions == null || versions.isEmpty()) {
+            return null;
+        } else {
+            Feature feature = versions.get(version);
+            if (feature == null && FeatureImpl.DEFAULT_VERSION.equals(version)) {
+                Version latest = new Version(cleanupVersion(version));
+                for (String available : versions.keySet()) {
+                    Version availableVersion = new Version(cleanupVersion(available));
+                    if (availableVersion.compareTo(latest) > 0) {
+                        feature = versions.get(available);
+                        latest = availableVersion;
+                    }
+                }
+            }
+            return feature;
+        }
+    }
+    
+    protected Map<String, Map<String, Feature>> getFeatures() throws Exception {
+        if (features == null) {
+            //the outer map's key is feature name, the inner map's key is feature version       
+            Map<String, Map<String, Feature>> map = new HashMap<String, Map<String, Feature>>();
+            // Two phase load:
+            // * first load dependent repositories
+            for (;;) {
+                boolean newRepo = false;
+                for (FeatureRepositoryImpl repo : listRepositories()) {
+                    for (URI uri : repo.getRepositories()) {
+                        if (!repositories.keySet().contains(uri)) {
+                            internalAddRepository(uri);
+                            newRepo = true;
+                        }
+                    }
+                }
+                if (!newRepo) {
+                    break;
+                }
+            }
+            // * then load all features
+            for (FeatureRepositoryImpl repo : repositories.values()) {
+                for (Feature f : repo.getFeatures()) {
+                    if (map.get(f.getName()) == null) {
+                        Map<String, Feature> versionMap = new HashMap<String, Feature>();
+                        versionMap.put(f.getVersion(), f);
+                        map.put(f.getName(), versionMap);
+                    } else {
+                        map.get(f.getName()).put(f.getVersion(), f);
+                    }
+                }
+            }
+            features = map;
+        }
+        return features;
+    }
+    
+    public FeatureRepositoryImpl[] listRepositories() {
+        Collection<FeatureRepositoryImpl> repos = repositories.values();
+        return repos.toArray(new FeatureRepositoryImpl[repos.size()]);
+    }
+    
+    protected Bundle installBundleIfNeeded(String bundleLocation) throws IOException, BundleException {
+        InputStream is = new BufferedInputStream(new URL(bundleLocation).openStream());
+        try {
+            is.mark(256 * 1024);
+            JarInputStream jar = new JarInputStream(is);
+            Manifest m = jar.getManifest();
+            String sn = m.getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
+            String vStr = m.getMainAttributes().getValue(Constants.BUNDLE_VERSION);
+            Version v = vStr == null ? Version.emptyVersion : Version.parseVersion(vStr);
+            for (Bundle b : bundleContext.getBundles()) {
+                if (b.getSymbolicName() != null && b.getSymbolicName().equals(sn)) {
+                    vStr = (String) b.getHeaders().get(Constants.BUNDLE_VERSION);
+                    Version bv = vStr == null ? Version.emptyVersion : Version.parseVersion(vStr);
+                    if (v.equals(bv)) {
+                        return b;
+                    }
+                }
+            }
+            try {
+                is.reset();
+            } catch (IOException e) {
+                is.close();
+                is = new BufferedInputStream(new URL(bundleLocation).openStream());
+            }
+            return getBundleContext().installBundle(bundleLocation, is);
+        } finally {
+            is.close();
+        }
+    }
+    
+    private BundleContext getBundleContext() {
+        return this.bundleContext;
+    }
+
+    /**
+     * Clean up version parameters. Other builders use more fuzzy definitions of
+     * the version syntax. This method cleans up such a version to match an OSGi
+     * version.
+     *
+     * @param version
+     * @return
+     */
+    static Pattern fuzzyVersion  = Pattern.compile("(\\d+)(\\.(\\d+)(\\.(\\d+))?)?([^a-zA-Z0-9](.*))?",
+                                                   Pattern.DOTALL);
+    static Pattern fuzzyModifier = Pattern.compile("(\\d+[.-])*(.*)",
+                                                   Pattern.DOTALL);
+    static public String cleanupVersion(String version) {
+        Matcher m = fuzzyVersion.matcher(version);
+        if (m.matches()) {
+            StringBuffer result = new StringBuffer();
+            String d1 = m.group(1);
+            String d2 = m.group(3);
+            String d3 = m.group(5);
+            String qualifier = m.group(7);
+
+            if (d1 != null) {
+                result.append(d1);
+                if (d2 != null) {
+                    result.append(".");
+                    result.append(d2);
+                    if (d3 != null) {
+                        result.append(".");
+                        result.append(d3);
+                        if (qualifier != null) {
+                            result.append(".");
+                            cleanupModifier(result, qualifier);
+                        }
+                    } else if (qualifier != null) {
+                        result.append(".0.");
+                        cleanupModifier(result, qualifier);
+                    }
+                } else if (qualifier != null) {
+                    result.append(".0.0.");
+                    cleanupModifier(result, qualifier);
+                }
+                return result.toString();
+            }
+        }
+        return version;
+    }
+
+    static void cleanupModifier(StringBuffer result, String modifier) {
+        Matcher m = fuzzyModifier.matcher(modifier);
+        if (m.matches())
+            modifier = m.group(2);
+
+        for (int i = 0; i < modifier.length(); i++) {
+            char c = modifier.charAt(i);
+            if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z')
+                    || (c >= 'A' && c <= 'Z') || c == '_' || c == '-')
+                result.append(c);
+        }
+    }
+
+}
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureRepositoryImpl.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureRepositoryImpl.java
new file mode 100644
index 0000000..fb296bb
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FeatureRepositoryImpl.java
@@ -0,0 +1,139 @@
+/*
+ * 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.servicemix.kernel.testing.support;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+
+
+/**
+ * The repository implementation.
+ */
+
+public class FeatureRepositoryImpl {
+
+    private URI uri;
+    private List<Feature> features;
+    private List<URI> repositories;
+
+    public FeatureRepositoryImpl(URI uri) {
+        this.uri = uri;
+    }
+
+    
+    public URI getURI() {
+        return uri;
+    }
+
+    public URI[] getRepositories() throws Exception {
+        if (repositories == null) {
+            load();
+        }
+        return repositories.toArray(new URI[repositories.size()]);
+    }
+
+    public Feature[] getFeatures() throws Exception {
+        if (features == null) {
+            load();
+        }
+        return features.toArray(new Feature[features.size()]);
+    }
+
+    public void load() throws IOException {
+        try {
+            repositories = new ArrayList<URI>();
+            features = new ArrayList<Feature>();
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+            Document doc = factory.newDocumentBuilder().parse(uri.toURL().openStream());
+            NodeList nodes = doc.getDocumentElement().getChildNodes();
+            for (int i = 0; i < nodes.getLength(); i++) {
+                Node node = nodes.item(i);
+                if (!(node instanceof Element)) {
+                    continue;
+                }
+                if ("repository".equals(node.getNodeName())) {
+                    Element e = (Element) nodes.item(i);
+                    repositories.add(new URI(e.getTextContent()));
+                } else if ("feature".equals(node.getNodeName())) {
+                    Element e = (Element) nodes.item(i);
+                    String name = e.getAttribute("name");
+                    String version = e.getAttribute("version");
+                    Feature f;
+                    if (version != null && version.length() > 0) {
+                        f = new FeatureImpl(name, version);
+                    } else {
+                        f = new FeatureImpl(name);
+                    }
+
+                    NodeList featureNodes = e.getElementsByTagName("feature");
+                    for (int j = 0; j < featureNodes.getLength(); j++) {
+                        Element b = (Element) featureNodes.item(j);
+                        String dependencyFeatureVersion = b.getAttribute("version");
+                        if (dependencyFeatureVersion != null && dependencyFeatureVersion.length() > 0) {
+                            f.addDependency(new FeatureImpl(b.getTextContent(), dependencyFeatureVersion));
+                        } else {
+                            f.addDependency(new FeatureImpl(b.getTextContent()));
+                        }
+                    }
+                    NodeList configNodes = e.getElementsByTagName("config");
+                    for (int j = 0; j < configNodes.getLength(); j++) {
+                        Element c = (Element) configNodes.item(j);
+                        String cfgName = c.getAttribute("name");
+                        String data = c.getTextContent();
+                        Properties properties = new Properties();
+                        properties.load(new ByteArrayInputStream(data.getBytes()));
+                        Map<String, String> hashtable = new Hashtable<String, String>();
+                        for (Object key : properties.keySet()) {
+                            String n = key.toString();
+                            hashtable.put(n, properties.getProperty(n));
+                        }
+                        f.addConfig(cfgName, hashtable);
+                    }
+                    NodeList bundleNodes = e.getElementsByTagName("bundle");
+                    for (int j = 0; j < bundleNodes.getLength(); j++) {
+                        Element b = (Element) bundleNodes.item(j);
+                        f.addBundle(b.getTextContent());
+                    }
+                    features.add(f);
+                }
+            }
+        } catch (SAXException e) {
+            throw (IOException) new IOException().initCause(e);
+        } catch (ParserConfigurationException e) {
+            throw (IOException) new IOException().initCause(e);
+        } catch (URISyntaxException e) {
+            throw (IOException) new IOException().initCause(e);
+        }
+    }
+
+}
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FrameworkUtil.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FrameworkUtil.java
new file mode 100644
index 0000000..e3427a9
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/FrameworkUtil.java
@@ -0,0 +1,31 @@
+/*
+ * 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.servicemix.kernel.testing.support;
+
+import org.apache.felix.framework.FilterImpl;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+
+/**
+ */
+public class FrameworkUtil {
+
+    public static Filter createFilter(String f) throws InvalidSyntaxException {
+        return new FilterImpl(f);
+    }
+
+}
diff --git a/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/SmxKernelPlatform.java b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/SmxKernelPlatform.java
new file mode 100644
index 0000000..eed2d5e
--- /dev/null
+++ b/karaf/testing/support/src/main/java/org/apache/servicemix/kernel/testing/support/SmxKernelPlatform.java
@@ -0,0 +1,428 @@
+/*
+ * 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.servicemix.kernel.testing.support;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.felix.framework.Felix;
+import org.apache.felix.framework.util.CompoundEnumeration;
+import org.apache.felix.framework.util.FelixConstants;
+import org.apache.servicemix.kernel.main.Main;
+import org.apache.servicemix.kernel.main.spi.MainService;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceRegistration;
+import org.springframework.osgi.test.platform.FelixPlatform;
+import org.springframework.osgi.test.platform.OsgiPlatform;
+import org.springframework.util.ClassUtils;
+
+public class SmxKernelPlatform implements OsgiPlatform {
+
+    private static final Log log = LogFactory.getLog(FelixPlatform.class);
+
+    private static final String FELIX_CONF_FILE = "config.properties";
+
+    private static final String FELIX_CONFIG_PROPERTY = "config.properties";
+
+    public final static String	FRAMEWORK_STORAGE = "org.osgi.framework.storage";
+
+    private BundleContext context;
+
+    private Object platform;
+
+    private File felixStorageDir;
+
+    private Properties configurationProperties = new Properties();
+
+    protected Properties getPlatformProperties() {
+        // load Felix configuration
+        Properties props = new Properties();
+        props.putAll(getFelixConfiguration());
+        props.putAll(getLocalConfiguration());
+        return props;
+    }
+
+    public Properties getConfigurationProperties() {
+        // local properties
+        configurationProperties.putAll(getPlatformProperties());
+        // system properties
+        configurationProperties.putAll(System.getProperties());
+        return configurationProperties;
+    }
+
+    public BundleContext getBundleContext() {
+        return context;
+    }
+
+    private Set<String> getJars(Class... classes) {
+        Set<String> jars = new HashSet<String>();
+        for (Class cl : classes) {
+            String name = cl.getName().replace('.', '/') + ".class";
+            URL url = (cl.getClassLoader() != null ? cl.getClassLoader() : getClass().getClassLoader()).getResource(name);
+            String path = url.toString();
+            if (path.startsWith("jar:")) {
+                path = path.substring(0, path.indexOf('!'));
+            } else {
+                path = path.substring(0, path.indexOf(name));
+            }
+            jars.add(path);
+        }
+        return jars;
+    }
+
+    public void start() throws Exception {
+        Set<String> jars = getJars(Felix.class);
+        ClassLoader classLoader = new GuardClassLoader(toURLs(jars.toArray(new String[jars.size()])), null);
+
+        BundleActivator activator = new BundleActivator() {
+            private ServiceRegistration registration;
+
+            public void start(BundleContext context) {
+                registration = context.registerService(MainService.class.getName(), new MainService() {
+                    public String[] getArgs() {
+                        return new String[0];
+                    }
+                    public int getExitCode() {
+                        return 0;
+                    }
+                    public void setExitCode(int exitCode) {
+                    }
+                }, null);
+            }
+
+            public void stop(BundleContext context) {
+                registration.unregister();
+            }
+        };
+        List<BundleActivator> activations = new ArrayList<BundleActivator>();
+        activations.add(activator);
+
+        Properties props = getConfigurationProperties();
+        props.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, activations);
+
+        Thread.currentThread().setContextClassLoader(classLoader);
+        Class cl = classLoader.loadClass(Felix.class.getName());
+        Constructor cns = cl.getConstructor(Map.class);
+        platform = cns.newInstance(props);
+        platform.getClass().getMethod("start").invoke(platform);
+
+        Bundle systemBundle = (Bundle) platform;
+
+        // call getBundleContext
+        final Method getContext = systemBundle.getClass().getMethod("getBundleContext", null);
+
+        AccessController.doPrivileged(new PrivilegedAction() {
+
+            public Object run() {
+                getContext.setAccessible(true);
+                return null;
+            }
+        });
+        context = (BundleContext) getContext.invoke(systemBundle, null);
+    }
+
+    public void stop() throws Exception {
+        try {
+            platform.getClass().getMethod("stop").invoke(platform);
+        }
+        finally {
+            // remove cache folder
+            delete(felixStorageDir);
+        }
+    }
+
+    public String toString() {
+        return getClass().getName();
+    }
+
+    File createTempDir(String suffix) {
+        if (suffix == null)
+            suffix = "osgi";
+        File tempFileName;
+
+        try {
+            tempFileName = File.createTempFile("org.sfw.osgi", suffix);
+        }
+        catch (IOException ex) {
+            if (log.isWarnEnabled()) {
+                log.warn("Could not create temporary directory, returning a temp folder inside the current folder", ex);
+            }
+            return new File("./tmp-test");
+        }
+
+        tempFileName.delete(); // we want it to be a directory...
+        File tempFolder = new File(tempFileName.getAbsolutePath());
+        tempFolder.mkdir();
+        return tempFolder;
+    }
+
+    /**
+     * Configuration settings for the OSGi test run.
+     *
+     * @return
+     */
+    private Properties getLocalConfiguration() {
+        Properties props = new Properties();
+        felixStorageDir = createTempDir("felix");
+        props.setProperty(FRAMEWORK_STORAGE, this.felixStorageDir.getAbsolutePath());
+        if (log.isTraceEnabled())
+            log.trace("felix storage dir is " + felixStorageDir.getAbsolutePath());
+
+        return props;
+    }
+
+    /**
+     * Loads Felix config.properties.
+     *
+     * <strong>Note</strong> the current implementation uses Felix's Main class
+     * to resolve placeholders as opposed to loading the properties manually
+     * (through JDK's Properties class or Spring's PropertiesFactoryBean).
+     *
+     * @return
+     */
+    // TODO: this method should be removed once Felix 1.0.2 is released
+    private Properties getFelixConfiguration() {
+        String location = "/".concat(ClassUtils.classPackageAsResourcePath(getClass())).concat("/").concat(FELIX_CONF_FILE);
+        URL url = getClass().getResource(location);
+        if (url == null)
+            throw new RuntimeException("cannot find felix configuration properties file:" + location);
+
+        // used with Main
+        System.getProperties().setProperty(FELIX_CONFIG_PROPERTY, url.toExternalForm());
+
+        // load config.properties (use Felix's Main for resolving placeholders)
+        Properties props = new Properties();
+        InputStream is = null;
+        try {
+            is = url.openConnection().getInputStream();
+            props.load(is);
+            is.close();
+        }
+        catch (FileNotFoundException ex) {
+            // Ignore file not found.
+        }
+        catch (Exception ex) {
+            System.err.println("Main: Error loading system properties from " + url);
+            System.err.println("Main: " + ex);
+            try {
+                if (is != null) is.close();
+            }
+            catch (IOException ex2) {
+                // Nothing we can do.
+            }
+            return null;
+        }
+        // Perform variable substitution for system properties.
+        for (Enumeration e = props.propertyNames(); e.hasMoreElements();) {
+            String name = (String) e.nextElement();
+            props.setProperty(name, Main.substVars(props.getProperty(name), name, null, props));
+        }
+        return props;
+    }
+
+    /**
+     * Delete the given file (can be a simple file or a folder).
+     *
+     * @param file the file to be deleted
+     * @return if the deletion succeded or not
+     */
+    public static boolean delete(File file) {
+
+        // bail out quickly
+        if (file == null)
+            return false;
+
+        // recursively delete children file
+        boolean success = true;
+
+        if (file.isDirectory()) {
+            String[] children = file.list();
+            for (int i = 0; i < children.length; i++) {
+                success &= delete(new File(file, children[i]));
+            }
+        }
+
+        // The directory is now empty so delete it
+        return (success &= file.delete());
+    }
+
+    private static URL[] toURLs(String[] jars) throws MalformedURLException {
+        URL[] urls = new URL[jars.length];
+        for (int i = 0; i < urls.length; i++) {
+            String s = jars[i];
+            if (s.startsWith("jar:")) {
+                s = s.substring("jar:".length());
+            }
+            urls[i] = new URL(s);
+        }
+        return urls;
+    }
+
+    public class GuardClassLoader extends URLClassLoader {
+        private Set<String> bootDelegationPackages = new HashSet<String>();
+        private Set<String> packages = new HashSet<String>();
+        private List<ClassLoader> parents = new ArrayList<ClassLoader>();
+
+        public GuardClassLoader(URL[] urls, List<String> additionalPackages) throws MalformedURLException {
+            super(urls, SmxKernelPlatform.class.getClassLoader());
+            Properties props = getConfigurationProperties();
+            String prop = props.getProperty("org.osgi.framework.system.packages");
+            String[] ps = prop.split(",");
+            for (String p : ps) {
+                String[] spack = p.split(";");
+                for (String sp : spack) {
+                    sp = sp.trim();
+                    if (!sp.startsWith("version")) {
+                        packages.add(sp);
+                    }
+                }
+            }
+            if (additionalPackages != null) {
+                packages.addAll(additionalPackages);
+            }
+            prop = props.getProperty("org.osgi.framework.bootdelegation");
+            ps = prop.split(",");
+            for (String p : ps) {
+                p = p.trim();
+                if (p.endsWith("*")) {
+                    p = p.substring(0, p.length() - 1);
+                }
+                bootDelegationPackages.add(p);
+            }
+            ClassLoader cl = getParent();
+            while (cl != null) {
+                parents.add(0, cl);
+                cl = cl.getParent();
+            }
+            //System.err.println("Boot packages: " + packages);
+        }
+
+        protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+            //System.err.println("Loading class: " + name);
+            Class c = findLoadedClass(name);
+            if (c == null) {
+                String pkg = name.substring(0, name.lastIndexOf('.'));
+                boolean match = name.startsWith("java.") || packages.contains(pkg);
+                if (!match) {
+                    for (String p : bootDelegationPackages) {
+                        if (pkg.startsWith(p)) {
+                            match = true;
+                            break;
+                        }
+                    }
+                }
+                if (match) {
+                    for (ClassLoader cl : parents) {
+                        try {
+                            c = cl.loadClass(name);
+                            //System.err.println("Class loaded from: " + cl.getResource(name.replace('.', '/') + ".class"));
+                            break;
+                        } catch (ClassNotFoundException e) {
+                        }
+                    }
+                    if (c == null) {
+                        throw new ClassNotFoundException(name);
+                    }
+                    //c = getParent().loadClass(name);
+                } else  {
+                    c = findClass(name);
+                }
+            }
+            if (resolve) {
+                resolveClass(c);
+            }
+            return c;
+        }
+
+        public URL getResource(String name) {
+            //System.err.println("GetResource: " + name);
+            URL url = getParent().getResource(name);
+            if (url != null && url.toString().startsWith("file:")) {
+                return url;
+            }
+            url = findResource(name);
+            System.err.println("Resource " + name + " found at " + url);
+            return url;
+            /*
+            URL u = getParent().getResource(name);
+            if (u != null) {
+                String path = u.toString();
+                int idx = path.indexOf('!');
+                if (idx > 0) {
+                    path = path.substring(0, idx);
+                    if (!jars.contains(path)) {
+                        return null;
+                    }
+                } else {
+                    idx = 0;
+                }
+            }
+            return u;
+            */
+        }
+
+        public Enumeration<URL> getResources(final String name) throws IOException {
+            //System.err.println("GetResources: " + name);
+            Enumeration[] tmp = new Enumeration[2];
+            final Enumeration<URL> e = getParent().getResources(name);
+            tmp[0] = new Enumeration<URL>() {
+                URL next = null;
+                public boolean hasMoreElements() {
+                    while (next == null && e.hasMoreElements()) {
+                        next = e.nextElement();
+                        String path = next.toString();
+                        if (!path.startsWith("file:")) {
+                            next = null;
+                        }
+                    }
+                    return next != null;
+                }
+                public URL nextElement() {
+                    return next;
+                }
+            };
+            tmp[1] = findResources(name);
+            return new CompoundEnumeration(tmp) {
+                public Object nextElement() {
+                    Object next = super.nextElement();
+                    System.err.println("Resources " + name + " found at " + next);
+                    return next;
+                }
+            };
+        }
+    }
+}
diff --git a/karaf/testing/support/src/test/java/org/apache/servicemix/kernel/testing/support/AbstractIntegrationTestTest.java b/karaf/testing/support/src/test/java/org/apache/servicemix/kernel/testing/support/AbstractIntegrationTestTest.java
new file mode 100644
index 0000000..9acba05
--- /dev/null
+++ b/karaf/testing/support/src/test/java/org/apache/servicemix/kernel/testing/support/AbstractIntegrationTestTest.java
@@ -0,0 +1,32 @@
+/*
+ * 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.servicemix.kernel.testing.support;
+
+import junit.framework.TestCase;
+
+
+public class AbstractIntegrationTestTest extends TestCase {
+
+    public void testSnapshotVersion() {
+        assertTrue(AbstractIntegrationTest.isTimestamped("0.9.0-20070713.230317-1"));
+        assertTrue(AbstractIntegrationTest.isSnapshot("0.9.0-SNAPSHOT"));
+        assertFalse(AbstractIntegrationTest.isSnapshot("0.9.0"));
+        assertEquals("0.9.0-SNAPSHOT", AbstractIntegrationTest.getSnapshot("0.9.0-20070713.230317-1"));
+        assertEquals("0.9.0", "0.9.0");
+    }
+
+}