FELIX-2033: Provide an easy to use layer for writing pax-exam test for Karaf
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@906029 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/karaf/itests/pom.xml b/karaf/itests/pom.xml
index abc44d4..022dfa9 100644
--- a/karaf/itests/pom.xml
+++ b/karaf/itests/pom.xml
@@ -65,6 +65,12 @@
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.felix.karaf.tooling</groupId>
+ <artifactId>org.apache.felix.karaf.tooling.testing</artifactId>
+ <scope>test</scope>
+ </dependency>
+
<!-- Pax EXAM -->
<dependency>
<groupId>org.ops4j.pax.exam</groupId>
diff --git a/karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/CoreTest.java b/karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/CoreTest.java
index 570f031..fb3d014 100644
--- a/karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/CoreTest.java
+++ b/karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/CoreTest.java
@@ -16,28 +16,32 @@
*/
package org.apache.felix.karaf.shell.itests;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Dictionary;
+import java.util.List;
+
+import org.apache.felix.karaf.testing.Helper;
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.ops4j.pax.exam.CoreOptions.bootClasspathLibrary;
-import static org.ops4j.pax.exam.CoreOptions.felix;
-import static org.ops4j.pax.exam.CoreOptions.maven;
-import static org.ops4j.pax.exam.CoreOptions.options;
-import static org.ops4j.pax.exam.CoreOptions.systemPackages;
-import static org.ops4j.pax.exam.CoreOptions.systemProperty;
-import static org.ops4j.pax.exam.CoreOptions.equinox;
-import static org.ops4j.pax.exam.CoreOptions.wrappedBundle;
import org.ops4j.pax.exam.Option;
-import static org.ops4j.pax.exam.OptionUtils.combine;
import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
import org.osgi.service.command.CommandProcessor;
import org.osgi.service.command.CommandSession;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.ops4j.pax.exam.CoreOptions.equinox;
+import static org.ops4j.pax.exam.CoreOptions.felix;
+import static org.ops4j.pax.exam.CoreOptions.options;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+import static org.ops4j.pax.exam.OptionUtils.combine;
+
@RunWith(JUnit4TestRunner.class)
-public class CoreTest extends AbstractIntegrationTest {
+public class CoreTest extends org.apache.felix.karaf.testing.AbstractIntegrationTest {
@Test
public void testHelp() throws Exception {
@@ -108,49 +112,17 @@
// }
//
@Configuration
- public static Option[] configuration() {
- Option[] options = options(
+ public static Option[] configuration() throws Exception {
+ Option[] options = combine(
+ // Default karaf environment
+ Helper.getDefaultOptions(),
// this is how you set the default log level when using pax logging (logProfile)
systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("DEBUG"),
- systemProperty("karaf.name").value("root"),
- systemProperty("karaf.home").value("target/karaf.home"),
- systemProperty("karaf.base").value("target/karaf.home"),
- systemProperty("karaf.startLocalConsole").value("false"),
- systemProperty("karaf.startRemoteShell").value("false"),
-
- // hack system packages
- systemPackages("org.apache.felix.karaf.jaas.boot;version=1.99"),
- bootClasspathLibrary(mavenBundle("org.apache.felix.karaf.jaas", "org.apache.felix.karaf.jaas.boot")).afterFramework(),
- bootClasspathLibrary(mavenBundle("org.apache.felix.karaf", "org.apache.felix.karaf.main")).afterFramework(),
-
- // Log
- mavenBundle("org.ops4j.pax.logging", "pax-logging-api"),
- mavenBundle("org.ops4j.pax.logging", "pax-logging-service"),
- // Felix Config Admin
- mavenBundle("org.apache.felix", "org.apache.felix.configadmin"),
- // Blueprint
- mavenBundle("org.apache.geronimo.blueprint", "geronimo-blueprint"),
-
- // Bundles
- mavenBundle("org.apache.mina", "mina-core"),
- mavenBundle("org.apache.sshd", "sshd-core"),
- mavenBundle("org.apache.felix.karaf.jaas", "org.apache.felix.karaf.jaas.config"),
- mavenBundle("org.apache.felix.gogo", "org.apache.felix.gogo.runtime"),
- mavenBundle("org.apache.felix.karaf.shell", "org.apache.felix.karaf.shell.console"),
- mavenBundle("org.apache.felix.karaf.shell", "org.apache.felix.karaf.shell.osgi"),
- mavenBundle("org.apache.felix.karaf.shell", "org.apache.felix.karaf.shell.log").noStart(),
-
+ // Test on both equinox and felix
equinox(), felix()
);
- // We need to add pax-exam-junit here when running with the ibm
- // jdk to avoid the following exception during the test run:
- // ClassNotFoundException: org.ops4j.pax.exam.junit.Configuration
- if ("IBM Corporation".equals(System.getProperty("java.vendor"))) {
- Option[] ibmOptions = options(
- wrappedBundle(maven("org.ops4j.pax.exam", "pax-exam-junit"))
- );
- options = combine(ibmOptions, options);
- }
+ // Stop the shell log bundle
+ Helper.findMaven(options, "org.apache.felix.karaf.shell", "org.apache.felix.karaf.shell.log").noStart();
return options;
}
diff --git a/karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/FeaturesTest.java b/karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/FeaturesTest.java
index ec5d198..49f5fc4 100644
--- a/karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/FeaturesTest.java
+++ b/karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/FeaturesTest.java
@@ -16,28 +16,26 @@
*/
package org.apache.felix.karaf.shell.itests;
-import static org.junit.Assert.assertNotNull;
+import org.apache.felix.karaf.testing.Helper;
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.ops4j.pax.exam.CoreOptions.bootClasspathLibrary;
-import static org.ops4j.pax.exam.CoreOptions.equinox;
-import static org.ops4j.pax.exam.CoreOptions.felix;
-import static org.ops4j.pax.exam.CoreOptions.maven;
-import static org.ops4j.pax.exam.CoreOptions.options;
-import static org.ops4j.pax.exam.CoreOptions.systemPackages;
-import static org.ops4j.pax.exam.CoreOptions.systemProperty;
-import static org.ops4j.pax.exam.CoreOptions.wrappedBundle;
import org.ops4j.pax.exam.Option;
-import static org.ops4j.pax.exam.OptionUtils.combine;
-import static org.ops4j.pax.exam.container.def.PaxRunnerOptions.scanFeatures;
import org.ops4j.pax.exam.junit.Configuration;
import org.ops4j.pax.exam.junit.JUnit4TestRunner;
import org.osgi.service.blueprint.container.BlueprintContainer;
import org.osgi.service.command.CommandProcessor;
import org.osgi.service.command.CommandSession;
+import static org.junit.Assert.assertNotNull;
+import static org.ops4j.pax.exam.CoreOptions.equinox;
+import static org.ops4j.pax.exam.CoreOptions.felix;
+import static org.ops4j.pax.exam.CoreOptions.maven;
+import static org.ops4j.pax.exam.CoreOptions.systemProperty;
+import static org.ops4j.pax.exam.OptionUtils.combine;
+import static org.ops4j.pax.exam.container.def.PaxRunnerOptions.scanFeatures;
+
@RunWith(JUnit4TestRunner.class)
-public class FeaturesTest extends AbstractIntegrationTest {
+public class FeaturesTest extends org.apache.felix.karaf.testing.AbstractIntegrationTest {
@Test
public void testFeatures() throws Exception {
@@ -53,57 +51,20 @@
}
@Configuration
- public static Option[] configuration() {
- Option[] options = options(
+ public static Option[] configuration() throws Exception{
+ return combine(
+ // Default karaf environment
+ Helper.getDefaultOptions(),
// this is how you set the default log level when using pax logging (logProfile)
systemProperty("org.ops4j.pax.logging.DefaultServiceLog.level").value("DEBUG"),
- systemProperty("karaf.name").value("root"),
- systemProperty("karaf.home").value("target/karaf.home"),
- systemProperty("karaf.base").value("target/karaf.home"),
- systemProperty("karaf.startLocalConsole").value("false"),
- systemProperty("karaf.startRemoteShell").value("false"),
-
- // hack system packages
- systemPackages("org.apache.felix.karaf.jaas.boot;version=1.99"),
- bootClasspathLibrary(mavenBundle("org.apache.felix.karaf.jaas", "org.apache.felix.karaf.jaas.boot")).afterFramework(),
- bootClasspathLibrary(mavenBundle("org.apache.felix.karaf", "org.apache.felix.karaf.main")).afterFramework(),
-
- // Log
- mavenBundle("org.ops4j.pax.logging", "pax-logging-api"),
- mavenBundle("org.ops4j.pax.logging", "pax-logging-service"),
- // Felix Config Admin
- mavenBundle("org.apache.felix", "org.apache.felix.configadmin"),
- // Felix Preferences Service
- mavenBundle("org.apache.felix", "org.apache.felix.prefs"),
- // Blueprint
- mavenBundle("org.apache.geronimo.blueprint", "geronimo-blueprint"),
-
- // Bundles
- mavenBundle("org.apache.mina", "mina-core"),
- mavenBundle("org.apache.sshd", "sshd-core"),
- mavenBundle("org.apache.felix.karaf.jaas", "org.apache.felix.karaf.jaas.config"),
- mavenBundle("org.apache.felix.karaf.shell", "org.apache.felix.karaf.shell.console"),
- mavenBundle("org.apache.felix.gogo", "org.apache.felix.gogo.runtime"),
- mavenBundle("org.apache.felix.karaf.shell", "org.apache.felix.karaf.shell.osgi"),
- mavenBundle("org.apache.felix.karaf.shell", "org.apache.felix.karaf.shell.log").noStart(),
-
+ // add two features
scanFeatures(
maven().groupId("org.apache.felix.karaf").artifactId("apache-felix-karaf").type("xml").classifier("features").versionAsInProject(),
"obr", "wrapper"
),
-
+ // Test on both equinox and felix
equinox(), felix()
);
- // We need to add pax-exam-junit here when running with the ibm
- // jdk to avoid the following exception during the test run:
- // ClassNotFoundException: org.ops4j.pax.exam.junit.Configuration
- if ("IBM Corporation".equals(System.getProperty("java.vendor"))) {
- Option[] ibmOptions = options(
- wrappedBundle(maven("org.ops4j.pax.exam", "pax-exam-junit"))
- );
- options = combine(ibmOptions, options);
- }
- return options;
}
}
diff --git a/karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/HeaderParser.java b/karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/HeaderParser.java
new file mode 100644
index 0000000..a6b6387
--- /dev/null
+++ b/karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/HeaderParser.java
@@ -0,0 +1,204 @@
+/**
+ * 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.felix.karaf.shell.itests;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility class to parse a standard OSGi header with paths.
+ *
+ * @author <a href="mailto:dev@geronimo.apache.org">Apache Geronimo Project</a>
+ * @version $Rev: 786132 $, $Date: 2009-06-18 17:47:58 +0200 (Thu, 18 Jun 2009) $
+ */
+public final class HeaderParser {
+
+ // Private constructor for static final class
+ private HeaderParser() {
+ }
+
+ /**
+ * Parse a given OSGi header into a list of paths
+ *
+ * @param header the OSGi header to parse
+ * @return the list of paths extracted from this header
+ */
+ public static List<PathElement> parseHeader(String header) {
+ List<PathElement> elements = new ArrayList<PathElement>();
+ if (header == null || header.trim().length() == 0) {
+ return elements;
+ }
+ String[] clauses = parseDelimitedString(header, ",");
+ for (String clause : clauses) {
+ String[] tokens = clause.split(";");
+ if (tokens.length < 1) {
+ throw new IllegalArgumentException("Invalid header clause: " + clause);
+ }
+ PathElement elem = new PathElement(tokens[0].trim());
+ elements.add(elem);
+ for (int i = 1; i < tokens.length; i++) {
+ int pos = tokens[i].indexOf('=');
+ if (pos != -1) {
+ if (pos > 0 && tokens[i].charAt(pos - 1) == ':') {
+ String name = tokens[i].substring(0, pos - 1).trim();
+ String value = tokens[i].substring(pos + 1).trim();
+ if (value.startsWith("\"") && value.endsWith("\"")) {
+ value = value.substring(1, value.length() - 1);
+ }
+ elem.addDirective(name, value);
+ } else {
+ String name = tokens[i].substring(0, pos).trim();
+ String value = tokens[i].substring(pos + 1).trim();
+ if (value.startsWith("\"") && value.endsWith("\"")) {
+ value = value.substring(1, value.length() - 1);
+ }
+ elem.addAttribute(name, value);
+ }
+ } else {
+ elem = new PathElement(tokens[i].trim());
+ elements.add(elem);
+ }
+ }
+ }
+ return elements;
+ }
+
+ /**
+ * Parses delimited string and returns an array containing the tokens. This
+ * parser obeys quotes, so the delimiter character will be ignored if it is
+ * inside of a quote. This method assumes that the quote character is not
+ * included in the set of delimiter characters.
+ * @param value the delimited string to parse.
+ * @param delim the characters delimiting the tokens.
+ * @return an array of string tokens or null if there were no tokens.
+ **/
+ public static String[] parseDelimitedString(String value, String delim)
+ {
+ if (value == null)
+ {
+ value = "";
+ }
+
+ List list = new ArrayList();
+
+ int CHAR = 1;
+ int DELIMITER = 2;
+ int STARTQUOTE = 4;
+ int ENDQUOTE = 8;
+
+ StringBuffer sb = new StringBuffer();
+
+ int expecting = (CHAR | DELIMITER | STARTQUOTE);
+
+ for (int i = 0; i < value.length(); i++)
+ {
+ char c = value.charAt(i);
+
+ boolean isDelimiter = (delim.indexOf(c) >= 0);
+ boolean isQuote = (c == '"');
+
+ if (isDelimiter && ((expecting & DELIMITER) > 0))
+ {
+ list.add(sb.toString().trim());
+ sb.delete(0, sb.length());
+ expecting = (CHAR | DELIMITER | STARTQUOTE);
+ }
+ else if (isQuote && ((expecting & STARTQUOTE) > 0))
+ {
+ sb.append(c);
+ expecting = CHAR | ENDQUOTE;
+ }
+ else if (isQuote && ((expecting & ENDQUOTE) > 0))
+ {
+ sb.append(c);
+ expecting = (CHAR | STARTQUOTE | DELIMITER);
+ }
+ else if ((expecting & CHAR) > 0)
+ {
+ sb.append(c);
+ }
+ else
+ {
+ throw new IllegalArgumentException("Invalid delimited string: " + value);
+ }
+ }
+
+ if (sb.length() > 0)
+ {
+ list.add(sb.toString().trim());
+ }
+
+ return (String[]) list.toArray(new String[list.size()]);
+ }
+
+ public static class PathElement {
+
+ private String path;
+ private Map<String, String> attributes;
+ private Map<String, String> directives;
+
+ public PathElement(String path) {
+ this.path = path;
+ this.attributes = new HashMap<String, String>();
+ this.directives = new HashMap<String, String>();
+ }
+
+ public String getName() {
+ return this.path;
+ }
+
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
+ public String getAttribute(String name) {
+ return attributes.get(name);
+ }
+
+ public void addAttribute(String name, String value) {
+ attributes.put(name, value);
+ }
+
+ public Map<String, String> getDirectives() {
+ return directives;
+ }
+
+ public String getDirective(String name) {
+ return directives.get(name);
+ }
+
+ public void addDirective(String name, String value) {
+ directives.put(name, value);
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder(this.path);
+ for (Map.Entry<String,String> directive : this.directives.entrySet()) {
+ sb.append(";").append(directive.getKey()).append(":=").append(directive.getValue());
+ }
+ for (Map.Entry<String,String> attribute : this.attributes.entrySet()) {
+ sb.append(";").append(attribute.getKey()).append("=").append(attribute.getValue());
+ }
+ return sb.toString();
+ }
+
+ }
+}
diff --git a/karaf/pom.xml b/karaf/pom.xml
index 187709a..ae3ff8f 100644
--- a/karaf/pom.xml
+++ b/karaf/pom.xml
@@ -309,6 +309,11 @@
<version>${pom.version}</version>
</dependency>
<dependency>
+ <groupId>org.apache.felix.karaf.tooling</groupId>
+ <artifactId>org.apache.felix.karaf.tooling.testing</artifactId>
+ <version>${pom.version}</version>
+ </dependency>
+ <dependency>
<groupId>org.apache.felix</groupId>
<artifactId>org.apache.felix.framework</artifactId>
<version>${felix.framework.version}</version>
diff --git a/karaf/tooling/pom.xml b/karaf/tooling/pom.xml
index e42b910..2e3b981 100644
--- a/karaf/tooling/pom.xml
+++ b/karaf/tooling/pom.xml
@@ -34,6 +34,7 @@
<modules>
<module>features-maven-plugin</module>
+ <module>testing</module>
</modules>
</project>
diff --git a/karaf/tooling/testing/pom.xml b/karaf/tooling/testing/pom.xml
new file mode 100644
index 0000000..4008c9b
--- /dev/null
+++ b/karaf/tooling/testing/pom.xml
@@ -0,0 +1,176 @@
+<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.felix.karaf.tooling</groupId>
+ <artifactId>tooling</artifactId>
+ <version>1.3.0-SNAPSHOT</version>
+ </parent>
+
+ <groupId>org.apache.felix.karaf.tooling</groupId>
+ <artifactId>org.apache.felix.karaf.tooling.testing</artifactId>
+ <packaging>bundle</packaging>
+ <version>1.3.0-SNAPSHOT</version>
+ <name>Apache Felix Karaf :: Testing environment</name>
+
+ <description>
+ A bundle to help using Pax-Exam and Karaf.
+ </description>
+
+ <properties>
+ <appendedResourcesDirectory>${basedir}/../../etc/appended-resources</appendedResourcesDirectory>
+ </properties>
+
+ <dependencies>
+ <!-- Pax EXAM -->
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-container-default</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.ops4j.pax.exam</groupId>
+ <artifactId>pax-exam-junit-extender-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.5</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <resources>
+ <resource>
+ <directory>${pom.basedir}/src/main/resources</directory>
+ <includes>
+ <include>**/*</include>
+ </includes>
+ </resource>
+ </resources>
+ <filters>
+ <filter>target/filter.txt</filter>
+ </filters>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>create-prop</id>
+ <phase>initialize</phase>
+ <configuration>
+ <tasks>
+ <taskdef resource="net/sf/antcontrib/antcontrib.properties" classpathref="maven.plugin.classpath" />
+ <property name="ant.regexp.regexpimpl" value="org.apache.tools.ant.util.regexp.Jdk14RegexpRegexp" />
+ <property name="mv" value="${project.version}" />
+ <echo message="Maven version: ${mv}" />
+ <propertyregex property="ov.p1" input="${mv}" regexp="(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:[^a-zA-Z0-9](.*))?" replace="\1" defaultValue="0" />
+ <propertyregex property="ov.p2" input="${mv}" regexp="(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:[^a-zA-Z0-9](.*))?" replace=".\2" defaultValue=".0" />
+ <propertyregex property="ov.p3" input="${mv}" regexp="(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:[^a-zA-Z0-9](.*))?" replace=".\3" defaultValue=".0" />
+ <propertyregex property="ov.p4" input="${mv}" regexp="(\d+)(?:\.(\d+)(?:\.(\d+))?)?(?:[^a-zA-Z0-9](.*))?" replace=".\4" defaultValue="" />
+ <propertyregex property="ov.p1a" input="${ov.p1}" regexp="(.+)" replace="\1" defaultValue="0" />
+ <propertyregex property="ov.p2a" input="${ov.p2}" regexp="(\..+)" replace="\1" defaultValue=".0" />
+ <propertyregex property="ov.p3a" input="${ov.p3}" regexp="(\..+)" replace="\1" defaultValue=".0" />
+ <propertyregex property="ov.p4a" input="${ov.p4}" regexp="(\..+)" replace="\1" defaultValue="" />
+ <property name="ov" value="${ov.p1a}${ov.p2a}${ov.p3a}${ov.p4a}" />
+ <echo message="OSGi version: ${ov}" />
+ <mkdir dir="target" />
+ <echo message="karaf.osgi.version = ${ov}" file="target/filter.txt" />
+ </tasks>
+ </configuration>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-filtered</id>
+ <!-- here the phase you need -->
+ <phase>compile</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${basedir}/target/classes/</outputDirectory>
+ <resources>
+ <resource>
+ <directory>../../assembly/src/main/filtered-resources/etc</directory>
+ <filtering>true</filtering>
+ <includes>
+ <include>config.properties</include>
+ <include>startup.properties</include>
+ </includes>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <!-- generate dependencies versions -->
+ <plugin>
+ <groupId>org.apache.servicemix.tooling</groupId>
+ <artifactId>depends-maven-plugin</artifactId>
+ <version>1.1</version>
+ <executions>
+ <execution>
+ <id>generate-depends-file</id>
+ <goals>
+ <goal>generate-depends-file</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <configuration>
+ <instructions>
+ <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+ <Export-Package>org.apache.felix.karaf.testing;version=${pom.version}</Export-Package>
+ <Import-Package>
+ !org.apache.felix.karaf.testing*,
+ *
+ </Import-Package>
+ <Include-Resource>
+ {maven-resources},
+ org/apache/felix/karaf/testing/config.properties=target/classes/config.properties,
+ org/apache/felix/karaf/testing/startup.properties=target/classes/startup.properties
+ </Include-Resource>
+ <_versionpolicy>${bnd.version.policy}</_versionpolicy>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/AbstractIntegrationTest.java b/karaf/tooling/testing/src/main/java/org/apache/felix/karaf/testing/AbstractIntegrationTest.java
similarity index 94%
rename from karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/AbstractIntegrationTest.java
rename to karaf/tooling/testing/src/main/java/org/apache/felix/karaf/testing/AbstractIntegrationTest.java
index 950a660..17177b6 100644
--- a/karaf/itests/src/test/java/org/apache/felix/karaf/shell/itests/AbstractIntegrationTest.java
+++ b/karaf/tooling/testing/src/main/java/org/apache/felix/karaf/testing/AbstractIntegrationTest.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.felix.karaf.shell.itests;
+package org.apache.felix.karaf.testing;
import java.util.Collection;
import java.util.Dictionary;
@@ -22,7 +22,6 @@
import java.util.LinkedList;
import java.util.List;
-import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Inject;
import org.ops4j.pax.exam.options.MavenArtifactProvisionOption;
import org.osgi.framework.Bundle;
@@ -34,6 +33,8 @@
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
+import static org.apache.felix.karaf.testing.Helper.mavenBundle;
+
public abstract class AbstractIntegrationTest {
public static final long DEFAULT_TIMEOUT = 30000;
@@ -104,10 +105,6 @@
return null;
}
- public static MavenArtifactProvisionOption mavenBundle(String groupId, String artifactId) {
- return CoreOptions.mavenBundle().groupId(groupId).artifactId(artifactId).versionAsInProject();
- }
-
/*
* Explode the dictionary into a ,-delimited list of key=value pairs
*/
@@ -137,4 +134,4 @@
return result;
}
-}
+}
\ No newline at end of file
diff --git a/karaf/tooling/testing/src/main/java/org/apache/felix/karaf/testing/Helper.java b/karaf/tooling/testing/src/main/java/org/apache/felix/karaf/testing/Helper.java
new file mode 100644
index 0000000..f41a716
--- /dev/null
+++ b/karaf/tooling/testing/src/main/java/org/apache/felix/karaf/testing/Helper.java
@@ -0,0 +1,706 @@
+/*
+ * 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.felix.karaf.testing;
+
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.ops4j.pax.exam.CoreOptions;
+import org.ops4j.pax.exam.Inject;
+import org.ops4j.pax.exam.Option;
+import org.ops4j.pax.exam.options.MavenArtifactProvisionOption;
+import org.ops4j.pax.exam.options.SystemPropertyOption;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
+
+import static org.ops4j.pax.exam.CoreOptions.bootClasspathLibrary;
+import static org.ops4j.pax.exam.CoreOptions.frameworkStartLevel;
+import static org.ops4j.pax.exam.CoreOptions.maven;
+import static org.ops4j.pax.exam.CoreOptions.wrappedBundle;
+import static org.ops4j.pax.exam.OptionUtils.combine;
+
+public final class Helper {
+
+ private Helper() {
+ }
+
+ public static MavenArtifactProvisionOption mavenBundle(String groupId, String artifactId) {
+ return CoreOptions.mavenBundle().groupId(groupId).artifactId(artifactId).versionAsInProject();
+ }
+
+ /*
+ * Explode the dictionary into a ,-delimited list of key=value pairs
+ */
+ private static String explode(Dictionary dictionary) {
+ Enumeration keys = dictionary.keys();
+ StringBuffer result = new StringBuffer();
+ while (keys.hasMoreElements()) {
+ Object key = keys.nextElement();
+ result.append(String.format("%s=%s", key, dictionary.get(key)));
+ if (keys.hasMoreElements()) {
+ result.append(", ");
+ }
+ }
+ return result.toString();
+ }
+
+ /*
+ * Provides an iterable collection of references, even if the original array is null
+ */
+ private static final Collection<ServiceReference> asCollection(ServiceReference[] references) {
+ List<ServiceReference> result = new LinkedList<ServiceReference>();
+ if (references != null) {
+ for (ServiceReference reference : references) {
+ result.add(reference);
+ }
+ }
+ return result;
+ }
+
+ private static final String DELIM_START = "${";
+ private static final String DELIM_STOP = "}";
+
+ /**
+ * <p>
+ * This method performs property variable substitution on the
+ * specified value. If the specified value contains the syntax
+ * <tt>${<prop-name>}</tt>, where <tt><prop-name></tt>
+ * refers to either a configuration property or a system property,
+ * then the corresponding property value is substituted for the variable
+ * placeholder. Multiple variable placeholders may exist in the
+ * specified value as well as nested variable placeholders, which
+ * are substituted from inner most to outer most. Configuration
+ * properties override system properties.
+ * </p>
+ *
+ * @param val The string on which to perform property substitution.
+ * @param currentKey The key of the property being evaluated used to
+ * detect cycles.
+ * @param cycleMap Map of variable references used to detect nested cycles.
+ * @param configProps Set of configuration properties.
+ * @return The value of the specified string after system property substitution.
+ * @throws IllegalArgumentException If there was a syntax error in the
+ * property placeholder syntax or a recursive variable reference.
+ */
+ private static String substVars(String val, String currentKey,
+ Map<String, String> cycleMap, Properties configProps)
+ throws IllegalArgumentException {
+ // If there is currently no cycle map, then create
+ // one for detecting cycles for this invocation.
+ if (cycleMap == null) {
+ cycleMap = new HashMap<String, String>();
+ }
+
+ // Put the current key in the cycle map.
+ cycleMap.put(currentKey, currentKey);
+
+ // Assume we have a value that is something like:
+ // "leading ${foo.${bar}} middle ${baz} trailing"
+
+ // Find the first ending '}' variable delimiter, which
+ // will correspond to the first deepest nested variable
+ // placeholder.
+ int stopDelim = val.indexOf(DELIM_STOP);
+
+ // Find the matching starting "${" variable delimiter
+ // by looping until we find a start delimiter that is
+ // greater than the stop delimiter we have found.
+ int startDelim = val.indexOf(DELIM_START);
+ while (stopDelim >= 0) {
+ int idx = val.indexOf(DELIM_START, startDelim + DELIM_START.length());
+ if ((idx < 0) || (idx > stopDelim)) {
+ break;
+ } else if (idx < stopDelim) {
+ startDelim = idx;
+ }
+ }
+
+ // If we do not have a start or stop delimiter, then just
+ // return the existing value.
+ if ((startDelim < 0) && (stopDelim < 0)) {
+ return val;
+ }
+ // At this point, we found a stop delimiter without a start,
+ // so throw an exception.
+ else if (((startDelim < 0) || (startDelim > stopDelim))
+ && (stopDelim >= 0)) {
+ throw new IllegalArgumentException(
+ "stop delimiter with no start delimiter: "
+ + val);
+ }
+
+ // At this point, we have found a variable placeholder so
+ // we must perform a variable substitution on it.
+ // Using the start and stop delimiter indices, extract
+ // the first, deepest nested variable placeholder.
+ String variable =
+ val.substring(startDelim + DELIM_START.length(), stopDelim);
+
+ // Verify that this is not a recursive variable reference.
+ if (cycleMap.get(variable) != null) {
+ throw new IllegalArgumentException(
+ "recursive variable reference: " + variable);
+ }
+
+ // Get the value of the deepest nested variable placeholder.
+ // Try to configuration properties first.
+ String substValue = (configProps != null)
+ ? configProps.getProperty(variable, null)
+ : null;
+ if (substValue == null) {
+ // Ignore unknown property values.
+ substValue = System.getProperty(variable, "");
+ }
+
+ // Remove the found variable from the cycle map, since
+ // it may appear more than once in the value and we don't
+ // want such situations to appear as a recursive reference.
+ cycleMap.remove(variable);
+
+ // Append the leading characters, the substituted value of
+ // the variable, and the trailing characters to get the new
+ // value.
+ val = val.substring(0, startDelim)
+ + substValue
+ + val.substring(stopDelim + DELIM_STOP.length(), val.length());
+
+ // Now perform substitution again, since there could still
+ // be substitutions to make.
+ val = substVars(val, currentKey, cycleMap, configProps);
+
+ // Return the value.
+ return val;
+ }
+
+ private static MavenArtifactProvisionOption convertToMaven(String location) {
+ String[] p = location.split("/");
+ if (p.length >= 4 && p[p.length-1].startsWith(p[p.length-3] + "-" + p[p.length-2])) {
+ MavenArtifactProvisionOption opt = new MavenArtifactProvisionOption();
+ int artifactIdVersionLength = p[p.length-3].length() + 1 + p[p.length-2].length(); // (artifactId + "-" + version).length
+ if (p[p.length-1].charAt(artifactIdVersionLength) == '-') {
+ opt.classifier((p[p.length-1].substring(artifactIdVersionLength + 1, p[p.length-1].lastIndexOf('.'))));
+ }
+ StringBuffer sb = new StringBuffer();
+ for (int j = 0; j < p.length - 3; j++) {
+ if (j > 0) {
+ sb.append('.');
+ }
+ sb.append(p[j]);
+ }
+ opt.groupId(sb.toString());
+ opt.artifactId(p[p.length-3]);
+ opt.version(p[p.length-2]);
+ opt.type(p[p.length-1].substring(p[p.length-1].lastIndexOf('.') + 1));
+ return opt;
+ } else {
+ throw new IllegalArgumentException("Unable to extract maven information from " + location);
+ }
+ }
+
+ public static Properties getDefaultSystemOptions() {
+ return getDefaultSystemOptions("target/karaf.home");
+ }
+
+ public static Properties getDefaultSystemOptions(String karafHome) {
+ Properties sysProps = new Properties();
+ sysProps.setProperty("karaf.name", "root");
+ sysProps.setProperty("karaf.home", karafHome);
+ sysProps.setProperty("karaf.base", karafHome);
+ sysProps.setProperty("karaf.startLocalConsole", "false");
+ sysProps.setProperty("karaf.startRemoteShell", "false");
+ sysProps.setProperty("org.osgi.framework.startlevel.beginning", "100");
+ return sysProps;
+ }
+
+ public static Option[] getDefaultConfigOptions() throws Exception {
+ return getDefaultConfigOptions(getDefaultSystemOptions(),
+ getResource("/org/apache/felix/karaf/testing/config.properties"));
+ }
+
+ public static Option[] getDefaultConfigOptions(Properties sysProps, URL configProperties) throws Exception {
+ // Load props
+ Properties configProps = new Properties();
+ InputStream is = configProperties.openStream();
+ try {
+ configProps.load(is);
+ } finally {
+ is.close();
+ }
+ // Set system props
+ for (Enumeration e = sysProps.propertyNames(); e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ configProps.setProperty(key, sysProps.getProperty(key));
+ }
+ // Perform variable substitution for system properties.
+ for (Enumeration e = configProps.propertyNames(); e.hasMoreElements();) {
+ String name = (String) e.nextElement();
+ configProps.setProperty(name, substVars(configProps.getProperty(name), name, null, configProps));
+ }
+ // Transform to sys props options
+ List<Option> options = new ArrayList<Option>();
+ for (Enumeration e = configProps.propertyNames(); e.hasMoreElements();) {
+ String name = (String) e.nextElement();
+ String value = configProps.getProperty(name);
+ value = value.replaceAll("\r", "").replaceAll("\n", "").replaceAll(" ", "");
+ options.add(new SystemPropertyOption(name).value(value));
+ System.err.println("sysprop: " + name + " = " + value);
+ }
+ if (configProps.getProperty("org.osgi.framework.startlevel.beginning") != null) {
+ options.add(frameworkStartLevel(Integer.parseInt(configProps.getProperty("org.osgi.framework.startlevel.beginning"))));
+ }
+ return options.toArray(new Option[options.size()]);
+ }
+
+ public static Option[] getDefaultProvisioningOptions() throws Exception {
+ return getDefaultProvisioningOptions(getDefaultSystemOptions(),
+ getResource("/org/apache/felix/karaf/testing/startup.properties"));
+ }
+
+ private static URL getResource(String location) throws Exception {
+ URL url = null;
+ if (Thread.currentThread().getContextClassLoader() != null) {
+ url = Thread.currentThread().getContextClassLoader().getResource(location);
+ }
+ if (url == null) {
+ url = Helper.class.getResource(location);
+ }
+ System.err.println("Trying to load resource: " + location + ". Found: " + url);
+ return url;
+ }
+
+ public static Option[] getDefaultProvisioningOptions(Properties sysProps, URL configProperties) throws Exception {
+ Properties startupProps = new Properties();
+ InputStream is = configProperties.openStream();
+ try {
+ startupProps.load(is);
+ } finally {
+ is.close();
+ }
+ // Perform variable substitution for system properties.
+ for (Enumeration e = startupProps.propertyNames(); e.hasMoreElements();) {
+ String name = (String) e.nextElement();
+ startupProps.setProperty(name, substVars(startupProps.getProperty(name), name, null, sysProps));
+ }
+ // Transform to sys props options
+ List<Option> options = new ArrayList<Option>();
+ options.add(bootClasspathLibrary(mavenBundle("org.apache.felix.karaf.jaas", "org.apache.felix.karaf.jaas.boot")).afterFramework());
+ options.add(bootClasspathLibrary(mavenBundle("org.apache.felix.karaf", "org.apache.felix.karaf.main")).afterFramework());
+ for (Enumeration e = startupProps.propertyNames(); e.hasMoreElements();) {
+ String name = (String) e.nextElement();
+ String value = startupProps.getProperty(name);
+ MavenArtifactProvisionOption opt = convertToMaven(name);
+ if (opt.getURL().contains("org.apache.felix.karaf.features")) {
+ opt.noStart();
+ }
+ opt.startLevel(Integer.parseInt(value));
+ options.add(opt);
+ }
+ options.add(mavenBundle("org.apache.felix.karaf.tooling", "org.apache.felix.karaf.tooling.testing"));
+ // We need to add pax-exam-junit here when running with the ibm
+ // jdk to avoid the following exception during the test run:
+ // ClassNotFoundException: org.ops4j.pax.exam.junit.Configuration
+ if ("IBM Corporation".equals(System.getProperty("java.vendor"))) {
+ options.add(wrappedBundle(maven("org.ops4j.pax.exam", "pax-exam-junit")));
+ }
+ return options.toArray(new Option[options.size()]);
+ }
+
+ public static Option[] getDefaultOptions() throws Exception {
+ return combine(getDefaultConfigOptions(), getDefaultProvisioningOptions());
+ }
+
+ public static MavenArtifactProvisionOption findMaven(Option[] options, String groupId, String artifactId) {
+ for (Option option : options) {
+ if (option instanceof MavenArtifactProvisionOption) {
+ MavenArtifactProvisionOption mvn = (MavenArtifactProvisionOption) option;
+ if (mvn.getURL().startsWith("mvn:" + groupId + "/" + artifactId + "/")) {
+ return mvn;
+ }
+ }
+ }
+ return null;
+ }
+
+ public abstract static class AbstractIntegrationTest {
+
+ public static final long DEFAULT_TIMEOUT = 30000;
+
+ @Inject
+ protected BundleContext bundleContext;
+
+ protected <T> T getOsgiService(Class<T> type, long timeout) {
+ return getOsgiService(type, null, timeout);
+ }
+
+ protected <T> T getOsgiService(Class<T> type) {
+ return getOsgiService(type, null, DEFAULT_TIMEOUT);
+ }
+
+ protected <T> T getOsgiService(Class<T> type, String filter, long timeout) {
+ ServiceTracker tracker = null;
+ try {
+ String flt;
+ if (filter != null) {
+ if (filter.startsWith("(")) {
+ flt = "(&(" + Constants.OBJECTCLASS + "=" + type.getName() + ")" + filter + ")";
+ } else {
+ flt = "(&(" + Constants.OBJECTCLASS + "=" + type.getName() + ")(" + filter + "))";
+ }
+ } else {
+ flt = "(" + Constants.OBJECTCLASS + "=" + type.getName() + ")";
+ }
+ Filter osgiFilter = FrameworkUtil.createFilter(flt);
+ tracker = new ServiceTracker(bundleContext, osgiFilter, null);
+ tracker.open(true);
+ // Note that the tracker is not closed to keep the reference
+ // This is buggy, as the service reference may change i think
+ Object svc = type.cast(tracker.waitForService(timeout));
+ if (svc == null) {
+ Dictionary dic = bundleContext.getBundle().getHeaders();
+ System.err.println("Test bundle headers: " + explode(dic));
+
+ for (ServiceReference ref : asCollection(bundleContext.getAllServiceReferences(null, null))) {
+ System.err.println("ServiceReference: " + ref);
+ }
+
+ for (ServiceReference ref : asCollection(bundleContext.getAllServiceReferences(null, flt))) {
+ System.err.println("Filtered ServiceReference: " + ref);
+ }
+
+ throw new RuntimeException("Gave up waiting for service " + flt);
+ }
+ return type.cast(svc);
+ } catch (InvalidSyntaxException e) {
+ throw new IllegalArgumentException("Invalid filter", e);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected Bundle installBundle(String groupId, String artifactId) throws Exception {
+ MavenArtifactProvisionOption mvnUrl = mavenBundle(groupId, artifactId);
+ return bundleContext.installBundle(mvnUrl.getURL());
+ }
+
+ protected Bundle getInstalledBundle(String symbolicName) {
+ for (Bundle b : bundleContext.getBundles()) {
+ if (b.getSymbolicName().equals(symbolicName)) {
+ return b;
+ }
+ }
+ return null;
+ }
+
+ public static MavenArtifactProvisionOption mavenBundle(String groupId, String artifactId) {
+ return CoreOptions.mavenBundle().groupId(groupId).artifactId(artifactId).versionAsInProject();
+ }
+
+ /*
+ * Explode the dictionary into a ,-delimited list of key=value pairs
+ */
+ private static String explode(Dictionary dictionary) {
+ Enumeration keys = dictionary.keys();
+ StringBuffer result = new StringBuffer();
+ while (keys.hasMoreElements()) {
+ Object key = keys.nextElement();
+ result.append(String.format("%s=%s", key, dictionary.get(key)));
+ if (keys.hasMoreElements()) {
+ result.append(", ");
+ }
+ }
+ return result.toString();
+ }
+
+ /*
+ * Provides an iterable collection of references, even if the original array is null
+ */
+ private static final Collection<ServiceReference> asCollection(ServiceReference[] references) {
+ List<ServiceReference> result = new LinkedList<ServiceReference>();
+ if (references != null) {
+ for (ServiceReference reference : references) {
+ result.add(reference);
+ }
+ }
+ return result;
+ }
+
+ private static final String DELIM_START = "${";
+ private static final String DELIM_STOP = "}";
+
+ /**
+ * <p>
+ * This method performs property variable substitution on the
+ * specified value. If the specified value contains the syntax
+ * <tt>${<prop-name>}</tt>, where <tt><prop-name></tt>
+ * refers to either a configuration property or a system property,
+ * then the corresponding property value is substituted for the variable
+ * placeholder. Multiple variable placeholders may exist in the
+ * specified value as well as nested variable placeholders, which
+ * are substituted from inner most to outer most. Configuration
+ * properties override system properties.
+ * </p>
+ *
+ * @param val The string on which to perform property substitution.
+ * @param currentKey The key of the property being evaluated used to
+ * detect cycles.
+ * @param cycleMap Map of variable references used to detect nested cycles.
+ * @param configProps Set of configuration properties.
+ * @return The value of the specified string after system property substitution.
+ * @throws IllegalArgumentException If there was a syntax error in the
+ * property placeholder syntax or a recursive variable reference.
+ */
+ private static String substVars(String val, String currentKey,
+ Map<String, String> cycleMap, Properties configProps)
+ throws IllegalArgumentException {
+ // If there is currently no cycle map, then create
+ // one for detecting cycles for this invocation.
+ if (cycleMap == null) {
+ cycleMap = new HashMap<String, String>();
+ }
+
+ // Put the current key in the cycle map.
+ cycleMap.put(currentKey, currentKey);
+
+ // Assume we have a value that is something like:
+ // "leading ${foo.${bar}} middle ${baz} trailing"
+
+ // Find the first ending '}' variable delimiter, which
+ // will correspond to the first deepest nested variable
+ // placeholder.
+ int stopDelim = val.indexOf(DELIM_STOP);
+
+ // Find the matching starting "${" variable delimiter
+ // by looping until we find a start delimiter that is
+ // greater than the stop delimiter we have found.
+ int startDelim = val.indexOf(DELIM_START);
+ while (stopDelim >= 0) {
+ int idx = val.indexOf(DELIM_START, startDelim + DELIM_START.length());
+ if ((idx < 0) || (idx > stopDelim)) {
+ break;
+ } else if (idx < stopDelim) {
+ startDelim = idx;
+ }
+ }
+
+ // If we do not have a start or stop delimiter, then just
+ // return the existing value.
+ if ((startDelim < 0) && (stopDelim < 0)) {
+ return val;
+ }
+ // At this point, we found a stop delimiter without a start,
+ // so throw an exception.
+ else if (((startDelim < 0) || (startDelim > stopDelim))
+ && (stopDelim >= 0)) {
+ throw new IllegalArgumentException(
+ "stop delimiter with no start delimiter: "
+ + val);
+ }
+
+ // At this point, we have found a variable placeholder so
+ // we must perform a variable substitution on it.
+ // Using the start and stop delimiter indices, extract
+ // the first, deepest nested variable placeholder.
+ String variable =
+ val.substring(startDelim + DELIM_START.length(), stopDelim);
+
+ // Verify that this is not a recursive variable reference.
+ if (cycleMap.get(variable) != null) {
+ throw new IllegalArgumentException(
+ "recursive variable reference: " + variable);
+ }
+
+ // Get the value of the deepest nested variable placeholder.
+ // Try to configuration properties first.
+ String substValue = (configProps != null)
+ ? configProps.getProperty(variable, null)
+ : null;
+ if (substValue == null) {
+ // Ignore unknown property values.
+ substValue = System.getProperty(variable, "");
+ }
+
+ // Remove the found variable from the cycle map, since
+ // it may appear more than once in the value and we don't
+ // want such situations to appear as a recursive reference.
+ cycleMap.remove(variable);
+
+ // Append the leading characters, the substituted value of
+ // the variable, and the trailing characters to get the new
+ // value.
+ val = val.substring(0, startDelim)
+ + substValue
+ + val.substring(stopDelim + DELIM_STOP.length(), val.length());
+
+ // Now perform substitution again, since there could still
+ // be substitutions to make.
+ val = substVars(val, currentKey, cycleMap, configProps);
+
+ // Return the value.
+ return val;
+ }
+
+ private static MavenArtifactProvisionOption convertToMaven(String location) {
+ String[] p = location.split("/");
+ if (p.length >= 4 && p[p.length-1].startsWith(p[p.length-3] + "-" + p[p.length-2])) {
+ MavenArtifactProvisionOption opt = new MavenArtifactProvisionOption();
+ int artifactIdVersionLength = p[p.length-3].length() + 1 + p[p.length-2].length(); // (artifactId + "-" + version).length
+ if (p[p.length-1].charAt(artifactIdVersionLength) == '-') {
+ opt.classifier((p[p.length-1].substring(artifactIdVersionLength + 1, p[p.length-1].lastIndexOf('.'))));
+ }
+ StringBuffer sb = new StringBuffer();
+ for (int j = 0; j < p.length - 3; j++) {
+ if (j > 0) {
+ sb.append('.');
+ }
+ sb.append(p[j]);
+ }
+ opt.groupId(sb.toString());
+ opt.artifactId(p[p.length-3]);
+ opt.version(p[p.length-2]);
+ opt.type(p[p.length-1].substring(p[p.length-1].lastIndexOf('.') + 1));
+ return opt;
+ } else {
+ throw new IllegalArgumentException("Unable to extract maven information from " + location);
+ }
+ }
+
+ public static Properties getDefaultSystemOptions() {
+ return getDefaultSystemOptions("target/karaf.home");
+ }
+
+ public static Properties getDefaultSystemOptions(String karafHome) {
+ Properties sysProps = new Properties();
+ sysProps.setProperty("karaf.name", "root");
+ sysProps.setProperty("karaf.home", karafHome);
+ sysProps.setProperty("karaf.base", karafHome);
+ sysProps.setProperty("karaf.startLocalConsole", "false");
+ sysProps.setProperty("karaf.startRemoteShell", "false");
+ sysProps.setProperty("org.osgi.framework.startlevel.beginning", "100");
+ return sysProps;
+ }
+
+ public static Option[] getDefaultConfigOptions() throws Exception {
+ return getDefaultConfigOptions(getDefaultSystemOptions(),
+ Helper.class.getResource("/config.properties"));
+ }
+
+ public static Option[] getDefaultConfigOptions(Properties sysProps, URL configProperties) throws Exception {
+ // Load props
+ Properties configProps = new Properties();
+ InputStream is = configProperties.openStream();
+ try {
+ configProps.load(is);
+ } finally {
+ is.close();
+ }
+ // Set system props
+ for (Enumeration e = sysProps.propertyNames(); e.hasMoreElements();) {
+ String key = (String) e.nextElement();
+ configProps.setProperty(key, sysProps.getProperty(key));
+ }
+ // Perform variable substitution for system properties.
+ for (Enumeration e = configProps.propertyNames(); e.hasMoreElements();) {
+ String name = (String) e.nextElement();
+ configProps.setProperty(name, substVars(configProps.getProperty(name), name, null, configProps));
+ }
+ // Transform to sys props options
+ List<Option> options = new ArrayList<Option>();
+ for (Enumeration e = configProps.propertyNames(); e.hasMoreElements();) {
+ String name = (String) e.nextElement();
+ String value = configProps.getProperty(name);
+ value = value.replaceAll("\r", "").replaceAll("\n", "").replaceAll(" ", "");
+ options.add(new SystemPropertyOption(name).value(value));
+ }
+ if (configProps.getProperty("org.osgi.framework.startlevel.beginning") != null) {
+ options.add(frameworkStartLevel(Integer.parseInt(configProps.getProperty("org.osgi.framework.startlevel.beginning"))));
+ }
+ return options.toArray(new Option[options.size()]);
+ }
+
+ public static Option[] getDefaultProvisioningOptions() throws Exception {
+ return getDefaultProvisioningOptions(getDefaultSystemOptions(),
+ Helper.class.getResource("/startup.properties"));
+ }
+
+ public static Option[] getDefaultProvisioningOptions(Properties sysProps, URL configProperties) throws Exception {
+ Properties startupProps = new Properties();
+ InputStream is = configProperties.openStream();
+ try {
+ startupProps.load(is);
+ } finally {
+ is.close();
+ }
+ // Perform variable substitution for system properties.
+ for (Enumeration e = startupProps.propertyNames(); e.hasMoreElements();) {
+ String name = (String) e.nextElement();
+ startupProps.setProperty(name, substVars(startupProps.getProperty(name), name, null, sysProps));
+ }
+ // Transform to sys props options
+ List<Option> options = new ArrayList<Option>();
+ options.add(bootClasspathLibrary(mavenBundle("org.apache.felix.karaf.jaas", "org.apache.felix.karaf.jaas.boot")).afterFramework());
+ options.add(bootClasspathLibrary(mavenBundle("org.apache.felix.karaf", "org.apache.felix.karaf.main")).afterFramework());
+ for (Enumeration e = startupProps.propertyNames(); e.hasMoreElements();) {
+ String name = (String) e.nextElement();
+ String value = startupProps.getProperty(name);
+ MavenArtifactProvisionOption opt = convertToMaven(name);
+ if (opt.getURL().contains("org.apache.felix.karaf.features")) {
+ opt.noStart();
+ }
+ opt.startLevel(Integer.parseInt(value));
+ options.add(opt);
+ }
+ // We need to add pax-exam-junit here when running with the ibm
+ // jdk to avoid the following exception during the test run:
+ // ClassNotFoundException: org.ops4j.pax.exam.junit.Configuration
+ if ("IBM Corporation".equals(System.getProperty("java.vendor"))) {
+ options.add(wrappedBundle(maven("org.ops4j.pax.exam", "pax-exam-junit")));
+ }
+ return options.toArray(new Option[options.size()]);
+ }
+
+ public static Option[] getDefaultOptions() throws Exception {
+ return combine(getDefaultConfigOptions(), getDefaultProvisioningOptions());
+ }
+
+ public static MavenArtifactProvisionOption findMaven(Option[] options, String groupId, String artifactId) {
+ for (Option option : options) {
+ if (option instanceof MavenArtifactProvisionOption) {
+ MavenArtifactProvisionOption mvn = (MavenArtifactProvisionOption) option;
+ if (mvn.getURL().startsWith("mvn:" + groupId + "/" + artifactId + "/")) {
+ return mvn;
+ }
+ }
+ }
+ return null;
+ }
+ }
+}
\ No newline at end of file