Commit the new iPOJO version (0.7.6).

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@642265 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/examples/junit4osgi/felix-command/metadata.xml b/ipojo/examples/junit4osgi/felix-command/metadata.xml
new file mode 100644
index 0000000..87f7db1
--- /dev/null
+++ b/ipojo/examples/junit4osgi/felix-command/metadata.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<iPOJO>

+	<component

+		className="org.apache.felix.ipojo.junit4osgi.command.JunitCommand"

+		factory="false">

+		<requires field="runner" />

+		<provides />

+	</component>

+	<instance

+		component="org.apache.felix.ipojo.junit4osgi.command.JunitCommand" />

+</iPOJO>
\ No newline at end of file
diff --git a/ipojo/examples/junit4osgi/felix-command/pom.xml b/ipojo/examples/junit4osgi/felix-command/pom.xml
new file mode 100644
index 0000000..f810d59
--- /dev/null
+++ b/ipojo/examples/junit4osgi/felix-command/pom.xml
@@ -0,0 +1,95 @@
+<!--

+	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.

+-->

+<project>

+	<parent>

+		<groupId>ipojo.examples</groupId>

+		<artifactId>Junit4Osgi</artifactId>

+		<version>0.7.6-SNAPSHOT</version>

+		<relativePath>../pom.xml</relativePath>

+	</parent>

+	<modelVersion>4.0.0</modelVersion>

+	<packaging>bundle</packaging>

+	<name>Junit4Osgi-Felix-Command</name>

+	<artifactId>

+		org.apache.felix.ipojo.junit4osgi.felix-command

+	</artifactId>

+	<dependencies>

+		<dependency>

+			<groupId>${pom.groupId}</groupId>

+			<artifactId>org.apache.felix.ipojo.junit4osgi</artifactId>

+			<version>${pom.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>org.apache.felix</groupId>

+			<artifactId>org.apache.felix.shell</artifactId>

+			<version>1.0.0</version>

+		</dependency>

+		<dependency>

+			<groupId>junit</groupId>

+			<artifactId>junit</artifactId>

+			<version>3.8.1</version>

+		</dependency>

+	</dependencies>

+	<build>

+		<plugins>

+			<plugin>

+				<groupId>org.apache.maven.plugins</groupId>

+				<artifactId>maven-compiler-plugin</artifactId>

+				<configuration>

+					<source>1.5</source>

+					<target>1.5</target>

+				</configuration>

+			</plugin>

+			<plugin>

+				<groupId>org.apache.felix</groupId>

+				<artifactId>maven-bundle-plugin</artifactId>

+				<version>1.4.0</version>

+				<extensions>true</extensions>

+				<configuration>

+					<instructions>

+						<Bundle-Name>

+							iPOJO OSGi Junit Runner - Felix Command

+						</Bundle-Name>

+						<Bundle-SymbolicName>

+							${pom.artifactId}

+						</Bundle-SymbolicName>

+						<Private-Package>

+							org.apache.felix.ipojo.junit4osgi.command

+						</Private-Package>

+					</instructions>

+				</configuration>

+			</plugin>

+			<plugin>

+				<groupId>org.apache.felix</groupId>

+				<artifactId>maven-ipojo-plugin</artifactId>

+				<version>${pom.version}</version>

+				<executions>

+					<execution>

+						<goals>

+							<goal>ipojo-bundle</goal>

+						</goals>

+						<configuration>

+							<ignoreAnnotations>true</ignoreAnnotations>

+						</configuration>

+					</execution>

+				</executions>

+			</plugin>

+		</plugins>

+	</build>

+</project>

diff --git a/ipojo/examples/junit4osgi/felix-command/src/main/java/org/apache/felix/ipojo/junit4osgi/command/JunitCommand.java b/ipojo/examples/junit4osgi/felix-command/src/main/java/org/apache/felix/ipojo/junit4osgi/command/JunitCommand.java
new file mode 100644
index 0000000..a217a9e
--- /dev/null
+++ b/ipojo/examples/junit4osgi/felix-command/src/main/java/org/apache/felix/ipojo/junit4osgi/command/JunitCommand.java
@@ -0,0 +1,111 @@
+/* 

+ * 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.ipojo.junit4osgi.command;

+

+import java.io.PrintStream;

+import java.util.ArrayList;

+import java.util.List;

+import java.util.ListIterator;

+

+import junit.framework.Test;

+import junit.framework.TestCase;

+import junit.framework.TestFailure;

+import junit.framework.TestResult;

+import junit.framework.TestSuite;

+

+import org.apache.felix.ipojo.junit4osgi.OSGiJunitRunner;

+import org.apache.felix.shell.Command;

+

+/**

+ * Felix shell command. Allow to run tests.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class JunitCommand implements Command {

+

+    private OSGiJunitRunner runner;

+

+    private List<String> getNamesFromTests(List<Test> list) {

+        List<String> names = new ArrayList<String>(list.size());

+        for (int i = 0; i < list.size(); i++) {

+            if (list.get(i) instanceof TestCase) {

+                names.add(((TestCase) list.get(i)).getName());

+            }

+            if (list.get(i) instanceof TestSuite) {

+                String name = ((TestSuite) list.get(i)).getName();

+                if (name == null) {

+                    name = ((TestSuite) list.get(i)).toString();

+                }

+                names.add(name);

+            }

+        }

+        return names;

+    }

+

+    public void execute(String line, PrintStream out, PrintStream err) {

+        line = line.substring(getName().length()).trim();

+        List<TestResult> tr = null;

+        if (line.equals("all")) {

+            if (runner.getTests() == null) {

+                err.println("No tests to execute");

+                return;

+            } else {

+                out.println("Executing " + getNamesFromTests(runner.getTests()));

+                tr = runner.run();

+            }

+        } else {

+            try {

+                Long bundleId = new Long(line);

+                if (runner.getTests(bundleId) == null) {

+                    err.println("No tests to execute");

+                    return;

+                } else {

+                    out.println("Executing " + getNamesFromTests(runner.getTests(bundleId)));

+                    tr = runner.run(bundleId);

+                }

+            } catch (NumberFormatException e) {

+                err.println("Unable to parse id " + line);

+                return;

+            }

+        }

+

+        ListIterator<TestResult> it = tr.listIterator();

+        while (it.hasNext()) {

+            TestResult result = it.next();

+            if (result.failureCount() != 0) {

+                TestFailure fail = (TestFailure) result.failures().nextElement();

+                out.println(fail.trace());

+                return;

+            }

+        }

+

+    }

+

+    public String getName() {

+        return "junit";

+    }

+

+    public String getShortDescription() {

+        return "launch junit tests";

+    }

+

+    public String getUsage() {

+        return "junit";

+    }

+

+}

diff --git a/ipojo/examples/junit4osgi/immediate-launcher/metadata.xml b/ipojo/examples/junit4osgi/immediate-launcher/metadata.xml
new file mode 100644
index 0000000..648ab67
--- /dev/null
+++ b/ipojo/examples/junit4osgi/immediate-launcher/metadata.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<iPOJO>

+	<component

+		className="org.apache.felix.ipojo.junit4osgi.command.ImmediateRunner"

+		factory="false">

+		<requires field="runner" />

+		<callback method="start" transition="validate" />

+	</component>

+	<instance

+		component="org.apache.felix.ipojo.junit4osgi.command.ImmediateRunner"

+		name="immediate-runner" />

+</iPOJO>
\ No newline at end of file
diff --git a/ipojo/examples/junit4osgi/immediate-launcher/pom.xml b/ipojo/examples/junit4osgi/immediate-launcher/pom.xml
new file mode 100644
index 0000000..18cfb31
--- /dev/null
+++ b/ipojo/examples/junit4osgi/immediate-launcher/pom.xml
@@ -0,0 +1,95 @@
+<!--

+	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.

+-->

+<project>

+	<parent>

+		<groupId>ipojo.examples</groupId>

+		<artifactId>Junit4Osgi</artifactId>

+		<version>0.7.6-SNAPSHOT</version>

+		<relativePath>../pom.xml</relativePath>

+	</parent>

+	<modelVersion>4.0.0</modelVersion>

+	<packaging>bundle</packaging>

+	<name>Junit4Osgi-Immediate-Runner</name>

+	<artifactId>

+		org.apache.felix.ipojo.junit4osgi.immediate-runner

+	</artifactId>

+	<dependencies>

+		<dependency>

+			<groupId>${pom.groupId}</groupId>

+			<artifactId>org.apache.felix.ipojo.junit4osgi</artifactId>

+			<version>${pom.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>org.apache.felix</groupId>

+			<artifactId>org.apache.felix.shell</artifactId>

+			<version>1.0.0</version>

+		</dependency>

+		<dependency>

+			<groupId>junit</groupId>

+			<artifactId>junit</artifactId>

+			<version>3.8.1</version>

+		</dependency>

+	</dependencies>

+	<build>

+		<plugins>

+			<plugin>

+				<groupId>org.apache.maven.plugins</groupId>

+				<artifactId>maven-compiler-plugin</artifactId>

+				<configuration>

+					<source>1.5</source>

+					<target>1.5</target>

+				</configuration>

+			</plugin>

+			<plugin>

+				<groupId>org.apache.felix</groupId>

+				<artifactId>maven-bundle-plugin</artifactId>

+				<version>1.4.0</version>

+				<extensions>true</extensions>

+				<configuration>

+					<instructions>

+						<Bundle-Name>

+							iPOJO OSGi Junit Runner - Immediate Runner

+						</Bundle-Name>

+						<Bundle-SymbolicName>

+							${pom.artifactId}

+						</Bundle-SymbolicName>

+						<Private-Package>

+							org.apache.felix.ipojo.junit4osgi.command

+						</Private-Package>

+					</instructions>

+				</configuration>

+			</plugin>

+			<plugin>

+				<groupId>org.apache.felix</groupId>

+				<artifactId>maven-ipojo-plugin</artifactId>

+				<version>${pom.version}</version>

+				<executions>

+					<execution>

+						<goals>

+							<goal>ipojo-bundle</goal>

+						</goals>

+						<configuration>

+							<ignoreAnnotations>true</ignoreAnnotations>

+						</configuration>

+					</execution>

+				</executions>

+			</plugin>

+		</plugins>

+	</build>

+</project>

diff --git a/ipojo/examples/junit4osgi/immediate-launcher/src/main/java/org/apache/felix/ipojo/junit4osgi/command/ImmediateRunner.java b/ipojo/examples/junit4osgi/immediate-launcher/src/main/java/org/apache/felix/ipojo/junit4osgi/command/ImmediateRunner.java
new file mode 100644
index 0000000..199cdf6
--- /dev/null
+++ b/ipojo/examples/junit4osgi/immediate-launcher/src/main/java/org/apache/felix/ipojo/junit4osgi/command/ImmediateRunner.java
@@ -0,0 +1,35 @@
+/* 

+ * 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.ipojo.junit4osgi.command;

+

+import org.apache.felix.ipojo.junit4osgi.OSGiJunitRunner;

+

+/**

+ * Felix shell command. Allow to run tests.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class ImmediateRunner {

+

+    private OSGiJunitRunner runner;

+    

+    public void start() {

+        runner.run();

+    }

+

+}

diff --git a/ipojo/examples/junit4osgi/junit4osgi/metadata.xml b/ipojo/examples/junit4osgi/junit4osgi/metadata.xml
new file mode 100644
index 0000000..83d84d3
--- /dev/null
+++ b/ipojo/examples/junit4osgi/junit4osgi/metadata.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<iPOJO xmlns:extender="org.apache.felix.ipojo.extender">

+	<Component

+		className="org.apache.felix.ipojo.junit4osgi.impl.JunitExtender">

+		<extender:extender extension="Test-Suite"

+			onArrival="onBundleArrival" onDeparture="onBundleDeparture" />

+		<callback transition="invalidate" method="stopping" />

+		<callback transition="validate" method="starting" />

+		<provides />

+	</Component>

+	<instance

+		component="org.apache.felix.ipojo.junit4osgi.impl.JunitExtender" />

+</iPOJO>
\ No newline at end of file
diff --git a/ipojo/examples/junit4osgi/junit4osgi/pom.xml b/ipojo/examples/junit4osgi/junit4osgi/pom.xml
new file mode 100644
index 0000000..e4ffbf4
--- /dev/null
+++ b/ipojo/examples/junit4osgi/junit4osgi/pom.xml
@@ -0,0 +1,103 @@
+<!--

+	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.

+-->

+<project>

+	<parent>

+		<groupId>ipojo.examples</groupId>

+		<artifactId>Junit4Osgi</artifactId>

+		<version>0.7.6-SNAPSHOT</version>

+		<relativePath>../pom.xml</relativePath>

+	</parent>

+	<modelVersion>4.0.0</modelVersion>

+	<packaging>bundle</packaging>

+	<name>Junit4Osgi</name>

+	<artifactId>org.apache.felix.ipojo.junit4osgi</artifactId>

+	<dependencies>

+		<dependency>

+			<groupId>org.apache.felix</groupId>

+			<artifactId>org.apache.felix.ipojo</artifactId>

+			<version>${pom.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>org.apache.felix</groupId>

+			<artifactId>org.apache.felix.ipojo.metadata</artifactId>

+			<version>${pom.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>junit</groupId>

+			<artifactId>junit</artifactId>

+			<version>3.8.1</version>

+		</dependency>

+	</dependencies>

+	<build>

+		<plugins>

+			<plugin>

+				<groupId>org.apache.maven.plugins</groupId>

+				<artifactId>maven-compiler-plugin</artifactId>

+				<configuration>

+					<source>1.5</source>

+					<target>1.5</target>

+				</configuration>

+			</plugin>

+			<plugin>

+				<groupId>org.apache.felix</groupId>

+				<artifactId>maven-bundle-plugin</artifactId>

+				<version>1.4.0</version>

+				<extensions>true</extensions>

+				<configuration>

+					<instructions>

+						<Bundle-Name>

+							iPOJO OSGi Junit Runner

+						</Bundle-Name>

+						<Bundle-SymbolicName>

+							${pom.artifactId}

+						</Bundle-SymbolicName>

+						<Private-Package>

+							org.apache.felix.ipojo.junit4osgi.impl,

+							org.apache.felix.ipojo.junit4osgi.test

+						</Private-Package>

+						<Export-Package>

+							org.apache.felix.ipojo.junit4osgi, junit.*

+						</Export-Package>

+						<Test-Suite>

+							org.apache.felix.ipojo.junit4osgi.test.TestTestCase,

+							org.apache.felix.ipojo.junit4osgi.test.TestOSGiTestCase,

+							org.apache.felix.ipojo.junit4osgi.test.TestTestSuite,

+							org.apache.felix.ipojo.junit4osgi.test.TestOSGiTestSuite

+						</Test-Suite>

+					</instructions>

+				</configuration>

+			</plugin>

+			<plugin>

+				<groupId>org.apache.felix</groupId>

+				<artifactId>maven-ipojo-plugin</artifactId>

+				<version>${pom.version}</version>

+				<executions>

+					<execution>

+						<goals>

+							<goal>ipojo-bundle</goal>

+						</goals>

+						<configuration>

+							<ignoreAnnotations>true</ignoreAnnotations>

+						</configuration>

+					</execution>

+				</executions>

+			</plugin>

+		</plugins>

+	</build>

+</project>

diff --git a/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/OSGiJunitRunner.java b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/OSGiJunitRunner.java
new file mode 100644
index 0000000..57a67d6
--- /dev/null
+++ b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/OSGiJunitRunner.java
@@ -0,0 +1,72 @@
+/* 

+ * 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.ipojo.junit4osgi;

+

+import java.io.PrintStream;

+import java.util.List;

+

+import junit.framework.Test;

+import junit.framework.TestResult;

+

+/**

+ * OSGi Junit Runner.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public interface OSGiJunitRunner {

+    

+    /**

+     * Set the output stream of the runner.

+     * @param ps : print stream.

+     */

+    void setResultPrinter(PrintStream ps);

+    

+    /**

+     * Run the tests.

+     * @return the list of TestResult.

+     */

+    List<TestResult>  run();

+    

+    /**

+     * Run the tests from the given bundle.

+     * @param bundleId : bundle containing the tests. 

+     * @return the list of the results.

+     */

+    List<TestResult> run(long bundleId);

+    

+    /**

+     * Get the list of available test suites.

+     * @return the list of test suites.

+     */

+    List<Test> getTests();

+    

+    /**

+     * Get the tests from the given bundle. 

+     * @param bundleId : bundle id.

+     * @return the list of test suites contained in the given bundle.

+     */

+    List<Test> getTests(long bundleId);

+    

+    /**

+     * Run the given test.

+     * @param test : test to execute.

+     * @return the result.

+     */

+    public TestResult run(Test test);

+

+}

diff --git a/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/OSGiTestCase.java b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/OSGiTestCase.java
new file mode 100644
index 0000000..e10ad04
--- /dev/null
+++ b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/OSGiTestCase.java
@@ -0,0 +1,187 @@
+/* 

+ * 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.ipojo.junit4osgi;

+

+import junit.framework.TestCase;

+

+import org.osgi.framework.BundleContext;

+

+/**

+ * OSGi Test Case. Allow the injection of the bundle context.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class OSGiTestCase extends TestCase {

+

+    protected BundleContext context;

+

+    public void setBundleContext(BundleContext bc) {

+        context = bc;

+    }

+

+    /**

+     * Asserts that two doubles are equal. If they are not an AssertionFailedError is thrown with the given message.

+     */

+    static public void assertEquals(String message, double expected, double actual) {

+        if (Double.compare(expected, actual) != 0) {

+            fail(formatEqualsMessage(message, expected, actual));

+        }

+    }

+

+    static String formatEqualsMessage(String message, Object expected, Object actual) {

+        String formatted = "";

+        if (message != null) {

+            formatted = message + " ";

+        }

+        return formatted + "expected:<" + expected + "> but was:<" + actual + ">";

+    }

+

+    static String formatNotEqualsMessage(String message, Object o1, Object o2) {

+        String formatted = "";

+        if (message != null) {

+            formatted = message + " ";

+        }

+        return formatted + "o1:<" + o1 + "> is equals to o2:<" + o2 + ">";

+    }

+

+    static String formatContainsMessage(String message, Object[] array, Object txt) {

+        String formatted = "";

+        if (message != null) {

+            formatted = message + " ";

+        }

+

+        String arr = null;

+        for (int i = 0; i < array.length; i++) {

+            if (arr == null) {

+                arr = "[" + array[i];

+            } else {

+                arr += "," + array[i];

+            }

+        }

+        arr += "]";

+

+        return formatted + "array:" + arr + " does not contains:<" + txt + ">";

+    }

+

+    static public void assertNotEquals(String message, Object o1, Object o2) {

+        if (o1.equals(o2)) {

+            fail(formatNotEqualsMessage(message, o1, o2));

+        }

+    }

+

+    static public void assertContains(String message, String[] array, String txt) {

+        for (int i = 0; i < array.length; i++) {

+            if (array[i].equals(txt)) {

+                return;

+            }

+        }

+        fail(formatContainsMessage(message, array, txt));

+    }

+

+    static public void assertContains(String message, byte[] array, int txt) {

+        for (int i = 0; i < array.length; i++) {

+            if (array[i] == txt) {

+                return;

+            }

+        }

+        Byte[] bytes = new Byte[array.length];

+        for (int i = 0; i < array.length; i++) {

+            bytes[i] = new Byte(array[i]);

+        }

+        fail(formatContainsMessage(message, bytes, txt));

+    }

+

+    static public void assertContains(String message, short[] array, int txt) {

+        for (int i = 0; i < array.length; i++) {

+            if (array[i] == txt) {

+                return;

+            }

+        }

+        Short[] bytes = new Short[array.length];

+        for (int i = 0; i < array.length; i++) {

+            bytes[i] = new Short(array[i]);

+        }

+        fail(formatContainsMessage(message, bytes, txt));

+    }

+

+    static public void assertContains(String message, int[] array, int txt) {

+        for (int i = 0; i < array.length; i++) {

+            if (array[i] == txt) {

+                return;

+            }

+        }

+        Integer[] bytes = new Integer[array.length];

+        for (int i = 0; i < array.length; i++) {

+            bytes[i] = new Integer(array[i]);

+        }

+        fail(formatContainsMessage(message, bytes, txt));

+    }

+

+    static public void assertContains(String message, long[] array, long txt) {

+        for (int i = 0; i < array.length; i++) {

+            if (array[i] == txt) {

+                return;

+            }

+        }

+        Long[] bytes = new Long[array.length];

+        for (int i = 0; i < array.length; i++) {

+            bytes[i] = new Long(array[i]);

+        }

+        fail(formatContainsMessage(message, bytes, txt));

+    }

+

+    static public void assertContains(String message, float[] array, float txt) {

+        for (int i = 0; i < array.length; i++) {

+            if (array[i] == txt) {

+                return;

+            }

+        }

+        Float[] bytes = new Float[array.length];

+        for (int i = 0; i < array.length; i++) {

+            bytes[i] = new Float(array[i]);

+        }

+        fail(formatContainsMessage(message, bytes, txt));

+    }

+

+    static public void assertContains(String message, double[] array, double txt) {

+        for (int i = 0; i < array.length; i++) {

+            if (array[i] == txt) {

+                return;

+            }

+        }

+        Double[] bytes = new Double[array.length];

+        for (int i = 0; i < array.length; i++) {

+            bytes[i] = new Double(array[i]);

+        }

+        fail(formatContainsMessage(message, bytes, txt));

+    }

+

+    static public void assertContains(String message, char[] array, char txt) {

+        for (int i = 0; i < array.length; i++) {

+            if (array[i] == txt) {

+                return;

+            }

+        }

+        Character[] bytes = new Character[array.length];

+        for (int i = 0; i < array.length; i++) {

+            bytes[i] = new Character(array[i]);

+        }

+        fail(formatContainsMessage(message, bytes, txt));

+    }

+

+}

diff --git a/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/OSGiTestSuite.java b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/OSGiTestSuite.java
new file mode 100644
index 0000000..8e7795a
--- /dev/null
+++ b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/OSGiTestSuite.java
@@ -0,0 +1,87 @@
+/* 

+ * 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.ipojo.junit4osgi;

+

+import junit.framework.Test;

+import junit.framework.TestCase;

+import junit.framework.TestResult;

+import junit.framework.TestSuite;

+

+import org.osgi.framework.BundleContext;

+

+/**

+ * OSGi Test Suite.

+ * Allow the injection of the bundle context.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class OSGiTestSuite extends TestSuite {

+

+    protected BundleContext context;

+

+    public OSGiTestSuite(Class clazz, BundleContext bc) {

+        super(clazz);

+        context = bc;

+    }

+

+    public OSGiTestSuite(BundleContext bc) {

+        super();

+        context = bc;

+    }

+

+    public OSGiTestSuite(String name, BundleContext bc) {

+        super(name);

+        context = bc;

+    }

+

+    public OSGiTestSuite(Class clazz, String name, BundleContext bc) {

+        super(clazz, name);

+        context = bc;

+    }

+

+    public void setBundleContext(BundleContext bc) {

+        context = bc;

+    }

+

+    /**

+     * Adds the tests from the given class to the suite

+     */

+    public void addTestSuite(Class testClass) {

+        if (OSGiTestCase.class.isAssignableFrom(testClass)) {

+            addTest(new OSGiTestSuite(testClass, context));

+        } else if (TestCase.class.isAssignableFrom(testClass)) {

+            addTest(new TestSuite(testClass));

+        } else {

+            System.out.println("Error : the " + testClass + " is not a valid test class");

+        }

+    }

+

+    public void runTest(Test test, TestResult result) {

+        if (test instanceof OSGiTestSuite) {

+            ((OSGiTestSuite) test).context = context;

+            test.run(result);

+        } else if (test instanceof OSGiTestCase) {

+            ((OSGiTestCase) test).context = context;

+            test.run(result);

+        } else {

+            test.run(result);

+        }

+

+    }

+

+}

diff --git a/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/impl/JunitExtender.java b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/impl/JunitExtender.java
new file mode 100644
index 0000000..34f0382
--- /dev/null
+++ b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/impl/JunitExtender.java
@@ -0,0 +1,302 @@
+/* 

+ * 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.ipojo.junit4osgi.impl;

+

+import java.io.PrintStream;

+import java.lang.reflect.Field;

+import java.lang.reflect.InvocationTargetException;

+import java.lang.reflect.Method;

+import java.lang.reflect.Modifier;

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.Iterator;

+import java.util.List;

+import java.util.Map;

+import java.util.Map.Entry;

+

+import junit.framework.Test;

+import junit.framework.TestResult;

+import junit.framework.TestSuite;

+

+import org.apache.felix.ipojo.junit4osgi.OSGiJunitRunner;

+import org.apache.felix.ipojo.junit4osgi.OSGiTestCase;

+import org.apache.felix.ipojo.junit4osgi.OSGiTestSuite;

+import org.osgi.framework.Bundle;

+import org.osgi.framework.BundleContext;

+

+/**

+ * Detect test suite from installed bundles.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class JunitExtender implements OSGiJunitRunner {

+

+    public static final String SUITE_METHODNAME = "suite";

+

+    private Map<Bundle, List<Class>> m_suites = new HashMap<Bundle, List<Class>>();

+

+    private ResultPrinter m_printer = new ResultPrinter(System.out);

+

+    void onBundleArrival(Bundle bundle, String header) {

+        String[] tss = header.split(",");

+        for (int i = 0; i < tss.length; i++) {

+            try {

+                if (tss[i].length() != 0) {

+                    System.out.println("Loading " + tss[i]);

+                    Class<? extends Test> clazz = bundle.loadClass(tss[i].trim());

+                    addTestSuite(bundle, clazz);

+                }

+            } catch (ClassNotFoundException e) {

+                System.err.println("The test suite " + tss[i] + " is not in the bundle " + bundle.getBundleId() + " : " + e.getMessage());

+            }

+        }

+    }

+

+    private synchronized void addTestSuite(Bundle bundle, Class<? extends Test> test) {

+        List<Class> list = m_suites.get(bundle);

+        if (list == null) {

+            list = new ArrayList<Class>();

+            list.add(test);

+            m_suites.put(bundle, list);

+        } else {

+            list.add(test);

+        }

+    }

+

+    private synchronized void removeTestSuites(Bundle bundle) {

+        m_suites.remove(bundle);

+    }

+

+    void onBundleDeparture(Bundle bundle) {

+        removeTestSuites(bundle);

+    }

+

+    public void setResultPrinter(PrintStream pw) {

+        m_printer = new ResultPrinter(pw);

+    }

+

+    public synchronized List<TestResult> run() {

+        List<TestResult> results = new ArrayList<TestResult>(m_suites.size());

+        Iterator<Entry<Bundle, List<Class>>> it = m_suites.entrySet().iterator();

+        while (it.hasNext()) {

+            Entry<Bundle, List<Class>> entry = it.next();

+            Bundle bundle = entry.getKey();

+            List<Class> list = m_suites.get(bundle);

+            for (int i = 0; i < list.size(); i++) {

+                Test test = createTestFromClass(list.get(i), bundle);

+                TestResult tr = doRun(test);

+                results.add(tr);

+            }

+        }

+        return results;

+    }

+

+    private TestResult doRun(Test test) {

+        TestResult result = new TestResult();

+        result.addListener(m_printer);

+        long startTime = System.currentTimeMillis();

+

+        test.run(result);

+

+        long endTime = System.currentTimeMillis();

+        long runTime = endTime - startTime;

+        m_printer.print(result, runTime);

+

+        return result;

+    }

+

+    private Test createTestFromClass(Class<?> clazz, Bundle bundle) {

+        Method suiteMethod = null;

+        boolean bc = false;

+        try {

+            suiteMethod = clazz.getMethod(SUITE_METHODNAME, new Class[0]);

+        } catch (Exception e) {

+            // try to use a suite method receiving a bundle context

+            try {

+                suiteMethod = clazz.getMethod(SUITE_METHODNAME, new Class[] { BundleContext.class });

+                bc = true;

+            } catch (Exception e2) {

+                // try to extract a test suite automatically

+                if (OSGiTestSuite.class.isAssignableFrom(clazz)) {

+                    OSGiTestSuite ts = new OSGiTestSuite(clazz, getBundleContext(bundle));

+                    return ts;

+                } else if (OSGiTestCase.class.isAssignableFrom(clazz)) {

+                    OSGiTestSuite ts = new OSGiTestSuite(clazz, getBundleContext(bundle));

+                    return ts;

+                } else {

+                    return new TestSuite(clazz);

+                }

+            }

+        }

+

+        if (!Modifier.isStatic(suiteMethod.getModifiers())) {

+            System.err.println("Suite() method must be static");

+            return null;

+        }

+        Test test = null;

+        try {

+            if (bc) {

+                test = (Test) suiteMethod.invoke(null, new Object[] { getBundleContext(bundle) }); // static method injection the bundle context

+            } else {

+                test = (Test) suiteMethod.invoke(null, (Object[]) new Class[0]); // static method

+            }

+        } catch (InvocationTargetException e) {

+            System.err.println("Failed to invoke suite():" + e.getTargetException().toString());

+            return null;

+        } catch (IllegalAccessException e) {

+            System.err.println("Failed to invoke suite():" + e.toString());

+            return null;

+        }

+

+        return test;

+    }

+

+    public synchronized List<Test> getTests() {

+        List<Test> results = new ArrayList<Test>();

+        Iterator<Entry<Bundle, List<Class>>> it = m_suites.entrySet().iterator();

+        while (it.hasNext()) {

+            Entry<Bundle, List<Class>> entry = it.next();

+            Bundle bundle = entry.getKey();

+            List<Class> list = m_suites.get(bundle);

+            for (int i = 0; i < list.size(); i++) {

+                Test test = createTestFromClass(list.get(i), bundle);

+                results.add(test);

+            }

+        }

+        return results;

+    }

+

+    public TestResult run(Test test) {

+        return doRun(test);

+    }

+

+    public synchronized List<Test> getTests(long bundleId) {

+        Iterator<Entry<Bundle, List<Class>>> it = m_suites.entrySet().iterator();

+        while (it.hasNext()) {

+            Entry<Bundle, List<Class>> entry = it.next();

+            Bundle bundle = entry.getKey();

+            if (bundle.getBundleId() == bundleId) {

+                List<Test> results = new ArrayList<Test>();

+                List<Class> list = m_suites.get(bundle);

+                for (int i = 0; i < list.size(); i++) {

+                    Test test = createTestFromClass(list.get(i), bundle);

+                    results.add(test);

+                }

+                return results;

+            }

+        }

+        return null;

+    }

+

+    public synchronized List<TestResult> run(long bundleId) {

+        Iterator<Entry<Bundle, List<Class>>> it = m_suites.entrySet().iterator();

+        while (it.hasNext()) {

+            Entry<Bundle, List<Class>> entry = it.next();

+            Bundle bundle = entry.getKey();

+            if (bundle.getBundleId() == bundleId) {

+                List<TestResult> results = new ArrayList<TestResult>();

+                List<Class> list = m_suites.get(bundle);

+                for (int i = 0; i < list.size(); i++) {

+                    Test test = createTestFromClass(list.get(i), bundle);

+                    TestResult tr = doRun(test);

+                    results.add(tr);

+                }

+                return results;

+            }

+        }

+        return null;

+    }

+

+    public synchronized void stopping() {

+        System.out.println("Cleaning test suites ...");

+        m_suites.clear();

+    }

+    

+    public void starting() {

+        System.out.println("Junit Extender starting ...");

+    }

+

+    private BundleContext getBundleContext(Bundle bundle) {

+        if (bundle == null) { return null; }

+

+        // getBundleContext (OSGi 4.1)

+        Method meth = null;

+        try {

+            meth = bundle.getClass().getMethod("getBundleContext", new Class[0]);

+        } catch (SecurityException e) {

+            // Nothing do to, will try the Equinox method

+        } catch (NoSuchMethodException e) {

+            // Nothing do to, will try the Equinox method

+        }

+

+        // try Equinox getContext if not found.

+        if (meth == null) {

+            try {

+                meth = bundle.getClass().getMethod("getContext", new Class[0]);

+            } catch (SecurityException e) {

+                // Nothing do to, will try field inspection

+            } catch (NoSuchMethodException e) {

+                // Nothing do to, will try field inspection

+            }

+        }

+

+        if (meth != null) {

+            if (! meth.isAccessible()) {

+                meth.setAccessible(true);

+            }

+            try {

+                return (BundleContext) meth.invoke(bundle, new Object[0]);

+            } catch (IllegalArgumentException e) {

+                err("Cannot get the BundleContext by invoking " + meth.getName(), e);

+                return null;

+            } catch (IllegalAccessException e) {

+                err("Cannot get the BundleContext by invoking " + meth.getName(), e);

+                return null;

+            } catch (InvocationTargetException e) {

+                err("Cannot get the BundleContext by invoking " + meth.getName(), e);

+                return null;

+            }

+        }

+

+        // Else : Field inspection (KF and Prosyst)        

+        Field[] fields = bundle.getClass().getDeclaredFields();

+        for (int i = 0; i < fields.length; i++) {

+            if (BundleContext.class.isAssignableFrom(fields[i].getType())) {

+                if (! fields[i].isAccessible()) {

+                    fields[i].setAccessible(true);

+                }

+                try {

+                    return (BundleContext) fields[i].get(bundle);

+                } catch (IllegalArgumentException e) {

+                    err("Cannot get the BundleContext by invoking " + meth.getName(), e);

+                    return null;

+                } catch (IllegalAccessException e) {

+                    err("Cannot get the BundleContext by invoking " + meth.getName(), e);

+                    return null;

+                }

+            }

+        }

+        err("Cannot find the BundleContext for " + bundle.getSymbolicName(), null);

+        return null;

+    }

+

+    private void err(String s, Throwable e) {

+        System.err.println(s + " : " + e.getMessage());

+    }

+

+}

diff --git a/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/impl/ResultPrinter.java b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/impl/ResultPrinter.java
new file mode 100644
index 0000000..4eb4e96
--- /dev/null
+++ b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/impl/ResultPrinter.java
@@ -0,0 +1,161 @@
+/* 
+ * 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.ipojo.junit4osgi.impl;
+
+import java.io.PrintStream;
+import java.text.NumberFormat;
+import java.util.Enumeration;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestFailure;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.runner.BaseTestRunner;
+
+/**
+ * Result Printer.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResultPrinter implements TestListener {
+    PrintStream fWriter;
+
+    int fColumn = 0;
+
+    public ResultPrinter(PrintStream writer) {
+        fWriter = writer;
+    }
+
+    /*
+     * API for use by textui.TestRunner
+     */
+
+    synchronized void print(TestResult result, long runTime) {
+        printHeader(runTime);
+        printErrors(result);
+        printFailures(result);
+        printFooter(result);
+    }
+
+    void printWaitPrompt() {
+        getWriter().println();
+        getWriter().println("<RETURN> to continue");
+    }
+
+    /*
+     * Internal methods
+     */
+
+    protected void printHeader(long runTime) {
+        getWriter().println();
+        getWriter().println("Time: " + elapsedTimeAsString(runTime));
+    }
+
+    protected void printErrors(TestResult result) {
+        printDefects(result.errors(), result.errorCount(), "error");
+    }
+
+    protected void printFailures(TestResult result) {
+        printDefects(result.failures(), result.failureCount(), "failure");
+    }
+
+    protected void printDefects(Enumeration<TestFailure> booBoos, int count, String type) {
+        if (count == 0)
+            return;
+        if (count == 1)
+            getWriter().println("There was " + count + " " + type + ":");
+        else
+            getWriter().println("There were " + count + " " + type + "s:");
+        for (int i = 1; booBoos.hasMoreElements(); i++) {
+            printDefect(booBoos.nextElement(), i);
+        }
+    }
+
+    public void printDefect(TestFailure booBoo, int count) { // only public for testing purposes
+        printDefectHeader(booBoo, count);
+        printDefectTrace(booBoo);
+    }
+
+    protected void printDefectHeader(TestFailure booBoo, int count) {
+        // I feel like making this a println, then adding a line giving the throwable a chance to print something
+        // before we get to the stack trace.
+        getWriter().print(count + ") " + booBoo.failedTest());
+    }
+
+    protected void printDefectTrace(TestFailure booBoo) {
+        getWriter().print(BaseTestRunner.getFilteredTrace(booBoo.trace()));
+    }
+
+    protected void printFooter(TestResult result) {
+        if (result.wasSuccessful()) {
+            getWriter().println();
+            getWriter().print("OK");
+            getWriter().println(" (" + result.runCount() + " test" + (result.runCount() == 1 ? "" : "s") + ")");
+
+        } else {
+            getWriter().println();
+            getWriter().println("FAILURES!!!");
+            getWriter().println("Tests run: " + result.runCount() + ",  Failures: " + result.failureCount() + ",  Errors: " + result.errorCount());
+        }
+        getWriter().println();
+    }
+
+    /**
+     * Returns the formatted string of the elapsed time. Duplicated from BaseTestRunner. Fix it.
+     */
+    protected String elapsedTimeAsString(long runTime) {
+        return NumberFormat.getInstance().format((double) runTime / 1000);
+    }
+
+    public PrintStream getWriter() {
+        return fWriter;
+    }
+
+    /**
+     * @see junit.framework.TestListener#addError(Test, Throwable)
+     */
+    public void addError(Test test, Throwable t) {
+        getWriter().print("E");
+    }
+
+    /**
+     * @see junit.framework.TestListener#addFailure(Test, AssertionFailedError)
+     */
+    public void addFailure(Test test, AssertionFailedError t) {
+        getWriter().print("F");
+    }
+
+    /**
+     * @see junit.framework.TestListener#endTest(Test)
+     */
+    public void endTest(Test test) {
+    }
+
+    /**
+     * @see junit.framework.TestListener#startTest(Test)
+     */
+    public void startTest(Test test) {
+        getWriter().print(".");
+        if (fColumn++ >= 40) {
+            getWriter().println();
+            fColumn = 0;
+        }
+    }
+
+}
diff --git a/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/test/TestOSGiTestCase.java b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/test/TestOSGiTestCase.java
new file mode 100644
index 0000000..2ee139e
--- /dev/null
+++ b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/test/TestOSGiTestCase.java
@@ -0,0 +1,34 @@
+/* 

+ * 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.ipojo.junit4osgi.test;

+

+import org.apache.felix.ipojo.junit4osgi.OSGiTestCase;

+

+/**

+ * Simple OSGi Test Case.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class TestOSGiTestCase extends OSGiTestCase {

+    

+    public void test1() {

+        System.out.println("Test BC");

+        assertNotNull("Test bundle context", context);

+    }

+

+}

diff --git a/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/test/TestOSGiTestSuite.java b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/test/TestOSGiTestSuite.java
new file mode 100644
index 0000000..c68f37c
--- /dev/null
+++ b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/test/TestOSGiTestSuite.java
@@ -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.

+ */

+package org.apache.felix.ipojo.junit4osgi.test;

+

+import junit.framework.Test;

+import junit.framework.TestSuite;

+

+import org.apache.felix.ipojo.junit4osgi.OSGiTestSuite;

+import org.osgi.framework.BundleContext;

+

+/**

+ * Simple OSGi Test suite.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class TestOSGiTestSuite extends TestSuite {

+    

+    public static Test suite(BundleContext bc) {

+        TestSuite ts = new TestSuite();

+        ts.setName("Test OSGi suite() method");

+        ts.addTestSuite(TestTestCase.class);

+        OSGiTestSuite ots = new OSGiTestSuite(TestOSGiTestCase.class, bc);

+        ots.setBundleContext(bc);

+        ts.addTest(ots);

+        return ts;

+    }

+

+}

diff --git a/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/test/TestTestCase.java b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/test/TestTestCase.java
new file mode 100644
index 0000000..0e1527b
--- /dev/null
+++ b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/test/TestTestCase.java
@@ -0,0 +1,37 @@
+/* 

+ * 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.ipojo.junit4osgi.test;

+

+import junit.framework.TestCase;

+

+/**

+ * Simple Test Case.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class TestTestCase extends TestCase {

+    

+    public void test1() { }

+    

+    public void test2() { }

+    

+    public void test3() { }

+    

+    public void test4() { }

+

+}

diff --git a/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/test/TestTestSuite.java b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/test/TestTestSuite.java
new file mode 100644
index 0000000..6bd1bf1
--- /dev/null
+++ b/ipojo/examples/junit4osgi/junit4osgi/src/main/java/org/apache/felix/ipojo/junit4osgi/test/TestTestSuite.java
@@ -0,0 +1,37 @@
+/* 

+ * 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.ipojo.junit4osgi.test;

+

+import junit.framework.Test;

+import junit.framework.TestSuite;

+

+/**

+ * Simple OSGi Test Suite.

+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>

+ */

+public class TestTestSuite extends TestSuite {

+    

+    public static Test suite() {

+        TestSuite ts = new TestSuite();

+        ts.setName("Test suite() method");

+        ts.addTestSuite(TestTestCase.class);

+        return ts;

+    }

+

+}

diff --git a/ipojo/examples/junit4osgi/metadata.xml b/ipojo/examples/junit4osgi/metadata.xml
new file mode 100644
index 0000000..29be5f3
--- /dev/null
+++ b/ipojo/examples/junit4osgi/metadata.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<iPOJO xmlns:extender="org.apache.felix.ipojo.extender">

+	<Component className="org.apache.felix.ipojo.junit4osgi.impl.JunitExtender">

+		<extender:extender extension="Test-Suite" onArrival="onBundleArrival" onDeparture="onBundleDeparture"/>

+		<callback transition="invalidate" method="stopping"/>

+		<provides/>

+	</Component>

+	<instance component="org.apache.felix.ipojo.junit4osgi.impl.JunitExtender"/>

+	

+	<component className="org.apache.felix.ipojo.junit4osgi.command.JunitCommand" factory="false">

+		<requires field="runner"/>

+		<provides/>

+	</component>

+	<instance component="org.apache.felix.ipojo.junit4osgi.command.JunitCommand"/>

+</iPOJO>
\ No newline at end of file
diff --git a/ipojo/examples/junit4osgi/pom.xml b/ipojo/examples/junit4osgi/pom.xml
new file mode 100644
index 0000000..4e5a05e
--- /dev/null
+++ b/ipojo/examples/junit4osgi/pom.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<project>

+  <parent>

+    <groupId>ipojo.examples</groupId>

+  	<artifactId>ipojo.examples</artifactId>

+  	<version>0.7.6-SNAPSHOT</version>

+    <relativePath>../pom.xml</relativePath>

+  </parent>

+  <modelVersion>4.0.0</modelVersion>

+  <artifactId>Junit4Osgi</artifactId>

+  <name>Apache Felix iPOJO Junit4OSGi</name>

+  <packaging>pom</packaging>

+

+  <profiles>

+	<profile>

+		<id>java5</id>

+		<activation>

+			<jdk>1.5</jdk>

+		</activation>

+		<modules>

+			<module>junit4osgi</module>

+			<module>felix-command</module>

+			<module>swing-runner</module>

+			<module>immediate-launcher</module>

+		</modules>

+	</profile>

+	<profile>

+		<id>java6</id>

+		<activation>

+			<jdk>1.6</jdk>

+		</activation>

+		<modules>

+			<module>junit4osgi</module>

+			<module>felix-command</module>

+			<module>swing-runner</module>

+			<module>immediate-launcher</module>

+		</modules>

+	</profile>

+  </profiles>

+</project>

diff --git a/ipojo/examples/junit4osgi/swing-runner/metadata.xml b/ipojo/examples/junit4osgi/swing-runner/metadata.xml
new file mode 100644
index 0000000..9251b6f
--- /dev/null
+++ b/ipojo/examples/junit4osgi/swing-runner/metadata.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>

+<iPOJO>

+	<component

+		classname="org.apache.felix.ipojo.junit4osgi.command.SwingRunner">

+		<requires field="m_runner" />

+		<callback method="stop" transition="invalidate" />

+		<callback method="start" transition="validate" />

+	</component>

+	<instance

+		component="org.apache.felix.ipojo.junit4osgi.command.SwingRunner" />

+</iPOJO>
\ No newline at end of file
diff --git a/ipojo/examples/junit4osgi/swing-runner/pom.xml b/ipojo/examples/junit4osgi/swing-runner/pom.xml
new file mode 100644
index 0000000..da9bae5
--- /dev/null
+++ b/ipojo/examples/junit4osgi/swing-runner/pom.xml
@@ -0,0 +1,105 @@
+<!--

+	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.

+-->

+<project>

+	<parent>

+		<groupId>ipojo.examples</groupId>

+		<artifactId>Junit4Osgi</artifactId>

+		<version>0.7.6-SNAPSHOT</version>

+		<relativePath>../pom.xml</relativePath>

+	</parent>

+	<modelVersion>4.0.0</modelVersion>

+	<packaging>bundle</packaging>

+	<name>Junit4Osgi-Swing-GUI</name>

+	<artifactId>org.apache.felix.ipojo.junit4osgi.swing-gui</artifactId>

+	<dependencies>

+		<dependency>

+			<groupId>${pom.groupId}</groupId>

+			<artifactId>org.apache.felix.ipojo.junit4osgi</artifactId>

+			<version>${pom.version}</version>

+		</dependency>

+		<dependency>

+			<groupId>org.apache.felix</groupId>

+			<artifactId>org.apache.felix.shell</artifactId>

+			<version>1.0.0</version>

+		</dependency>

+		<dependency>

+			<groupId>junit</groupId>

+			<artifactId>junit</artifactId>

+			<version>3.8.1</version>

+		</dependency>

+	</dependencies>

+	<build>

+		<plugins>

+			<plugin>

+				<groupId>org.apache.maven.plugins</groupId>

+				<artifactId>maven-compiler-plugin</artifactId>

+				<configuration>

+					<source>1.5</source>

+					<target>1.5</target>

+				</configuration>

+			</plugin>

+			<plugin>

+				<groupId>org.apache.felix</groupId>

+				<artifactId>maven-bundle-plugin</artifactId>

+				<version>1.4.0</version>

+				<extensions>true</extensions>

+				<configuration>

+					<instructions>

+						<Bundle-Name>

+							iPOJO OSGi Junit Runner - Swing Gui

+						</Bundle-Name>

+						<Bundle-SymbolicName>

+							${pom.artifactId}

+						</Bundle-SymbolicName>

+						<Private-Package>

+							org.apache.felix.ipojo.junit4osgi.command

+						</Private-Package>

+					</instructions>

+				</configuration>

+			</plugin>

+			<plugin>

+				<groupId>org.apache.felix</groupId>

+				<artifactId>maven-ipojo-plugin</artifactId>

+				<version>${pom.version}</version>

+				<executions>

+					<execution>

+						<goals>

+							<goal>ipojo-bundle</goal>

+						</goals>

+						<configuration>

+							<ignoreAnnotations>true</ignoreAnnotations>

+						</configuration>

+					</execution>

+				</executions>

+			</plugin>

+		</plugins>

+	</build>

+	<repositories>

+		<repository>

+			<id>unknown-jars-temp-repo</id>

+			<name>

+				A temporary repository created by NetBeans for libraries

+				and jars it could not identify. Please replace the

+				dependencies in this repository with correct ones and

+				delete this repository.

+			</name>

+			<url>file:${project.basedir}/lib</url>

+		</repository>

+	</repositories>

+</project>

diff --git a/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/ResultCellRenderer.java b/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/ResultCellRenderer.java
new file mode 100644
index 0000000..680ad32
--- /dev/null
+++ b/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/ResultCellRenderer.java
@@ -0,0 +1,75 @@
+/*
+ *  Copyright 2008 clement.
+ * 
+ *  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.
+ *  under the License.
+ */
+
+/* 
+ * 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.ipojo.junit4osgi.command;
+
+import java.awt.Color;
+import java.awt.Component;
+
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableCellRenderer;
+
+/**
+ * Test result renderer.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResultCellRenderer extends DefaultTableCellRenderer {
+
+    /**
+     * UUID.
+     */
+    private static final long serialVersionUID = 1L;
+
+    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
+        Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+
+        ResultTableModel results = (ResultTableModel) table.getModel();
+        String status = (String) results.getValueAt(row, column);
+        if (status.equals(ResultTableModel.SUCESS)) {
+            c.setForeground(Color.GREEN);
+            setToolTipText(results.getMessage(row, column));
+        }
+        if (status.equals(ResultTableModel.FAILURE)) {
+            c.setForeground(Color.ORANGE);
+            setToolTipText(results.getMessage(row, column));
+        }
+        if (status.equals(ResultTableModel.ERROR)) {
+            c.setForeground(Color.RED);
+            setToolTipText(results.getMessage(row, column));
+        }
+
+        return c;
+    }
+}
diff --git a/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/ResultTableModel.java b/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/ResultTableModel.java
new file mode 100644
index 0000000..491f223
--- /dev/null
+++ b/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/ResultTableModel.java
@@ -0,0 +1,183 @@
+/* 
+ * 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.ipojo.junit4osgi.command;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.table.AbstractTableModel;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+/**
+ * Result Table Model.
+ * Store the results of executed tests.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class ResultTableModel extends AbstractTableModel {
+
+    public static final String SUCESS = "success";
+
+    public static final String FAILURE = "failure";
+
+    public static final String ERROR = "error";
+
+    private static final long serialVersionUID = 1L;
+
+    private List<TestRecord> results = new ArrayList<TestRecord>();
+
+    public int getRowCount() {
+        return results.size();
+    }
+
+    public int getColumnCount() {
+        return 2;
+    }
+
+    public void addTest(Test t, AssertionFailedError e) {
+        TestRecord rec = new TestRecord(t, e);
+        results.add(rec);
+        fireTableDataChanged();
+    }
+
+    public void addTest(Test t, Throwable e) {
+        TestRecord rec = new TestRecord(t, e);
+        results.add(rec);
+        fireTableDataChanged();
+    }
+
+    public void addTest(Test t) {
+        if (!contains(t)) {
+            TestRecord rec = new TestRecord(t);
+            results.add(rec);
+            fireTableDataChanged();
+        }
+    }
+
+    public int getTestCount() {
+        return results.size();
+    }
+
+    public int getSucess() {
+        int count = 0;
+        for (TestRecord test : results) {
+            if (test.m_wasSucessFull) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    public int getErrors() {
+        int count = 0;
+        for (TestRecord test : results) {
+            if (test.m_error != null) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    public int getFailures() {
+        int count = 0;
+        for (TestRecord test : results) {
+            if (test.m_failure != null) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    private boolean contains(Test t) {
+        for (TestRecord test : results) {
+            if (test.m_test.equals(t)) { return true; }
+        }
+        return false;
+    }
+
+    public void clear() {
+        results.clear();
+        fireTableDataChanged();
+    }
+
+    public Object getValueAt(int rowIndex, int columnIndex) {
+        if (columnIndex == 0) { return results.get(rowIndex).m_test; }
+        if (columnIndex == 1) {
+            TestRecord tr = results.get(rowIndex);
+            if (tr.m_wasSucessFull) { return SUCESS; }
+            if (tr.m_failure != null) { return FAILURE; }
+            if (tr.m_error != null) { return ERROR; }
+        }
+        return null;
+    }
+
+    public String getColumnName(int column) {
+        if (column == 0) { return "Test"; }
+
+        if (column == 1) { return "Status"; }
+
+        return null;
+    }
+
+    public String getMessage(int row, int column) {
+        if (row == -1) { return null; }
+        TestRecord rec = results.get(row);
+        if (rec.m_wasSucessFull) { return "The test " + rec.m_test + " was executed sucessfully."; }
+        if (rec.m_failure != null) { return "The test " + rec.m_test + " has failed : \n" + rec.m_failure.getMessage(); }
+        if (rec.m_error != null) {
+            String message = "The test " + rec.m_test + " has thrown an error : \n" + rec.m_error.getMessage();
+            StringWriter sw = new StringWriter();
+            rec.m_error.printStackTrace(new PrintWriter(sw));
+            message += "\n" + sw.toString();
+            return message;
+        }
+        return "";
+    }
+
+    private class TestRecord {
+        private boolean m_wasSucessFull;
+
+        private Test m_test;
+
+        private AssertionFailedError m_failure;
+
+        private Throwable m_error;
+
+        public TestRecord(Test t, AssertionFailedError e) {
+            m_test = t;
+            m_wasSucessFull = false;
+            m_failure = e;
+        }
+
+        public TestRecord(Test t, Throwable e) {
+            m_test = t;
+            m_wasSucessFull = false;
+            m_error = e;
+        }
+
+        public TestRecord(Test t) {
+            m_test = t;
+            m_wasSucessFull = true;
+        }
+    }
+
+}
diff --git a/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/SwingRunner.form b/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/SwingRunner.form
new file mode 100644
index 0000000..55cf564
--- /dev/null
+++ b/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/SwingRunner.form
@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<Form version="1.3" maxVersion="1.5" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
+  <NonVisualComponents>
+    <Container class="javax.swing.JDialog" name="m_resultDialog">
+      <Properties>
+        <Property name="defaultCloseOperation" type="int" value="0"/>
+        <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[320, 250]"/>
+        </Property>
+      </Properties>
+      <Events>
+        <EventHandler event="windowClosed" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="onDialogClosed"/>
+        <EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="onDialogClosed"/>
+      </Events>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
+      <SubComponents>
+        <Container class="javax.swing.JScrollPane" name="m_message">
+          <Properties>
+            <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
+              <Border info="null"/>
+            </Property>
+            <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[300, 202]"/>
+            </Property>
+            <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[300, 202]"/>
+            </Property>
+          </Properties>
+          <AuxValues>
+            <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+          </AuxValues>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
+              <BorderConstraints direction="Center"/>
+            </Constraint>
+          </Constraints>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JTextArea" name="m_messageArea">
+              <Properties>
+                <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
+                  <Color blue="f0" green="f0" id="Panel.background" palette="3" red="f0" type="palette"/>
+                </Property>
+                <Property name="columns" type="int" value="20"/>
+                <Property name="editable" type="boolean" value="false"/>
+                <Property name="lineWrap" type="boolean" value="true"/>
+                <Property name="rows" type="int" value="5"/>
+                <Property name="wrapStyleWord" type="boolean" value="true"/>
+                <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+                  <Dimension value="[300, 250]"/>
+                </Property>
+                <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+                  <Dimension value="[250, 200]"/>
+                </Property>
+              </Properties>
+            </Component>
+          </SubComponents>
+        </Container>
+        <Component class="javax.swing.JButton" name="m_ok">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Ok"/>
+            <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[120, 23]"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="m_okActionPerformed"/>
+          </Events>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
+              <BorderConstraints direction="South"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+      </SubComponents>
+    </Container>
+  </NonVisualComponents>
+  <Properties>
+    <Property name="defaultCloseOperation" type="int" value="2"/>
+    <Property name="title" type="java.lang.String" value="Junit Runner"/>
+    <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+      <Dimension value="[580, 580]"/>
+    </Property>
+  </Properties>
+  <SyntheticProperties>
+    <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
+  </SyntheticProperties>
+  <AuxValues>
+    <AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
+    <AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
+    <AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
+    <AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
+    <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,2,84,0,0,2,98"/>
+  </AuxValues>
+
+  <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+  <SubComponents>
+    <Container class="javax.swing.JPanel" name="m_panel">
+      <Properties>
+        <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[580, 560]"/>
+        </Property>
+        <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+          <Dimension value="[580, 566]"/>
+        </Property>
+      </Properties>
+      <Constraints>
+        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+          <GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="2" fill="1" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="3" insetsBottom="3" insetsRight="3" anchor="10" weightX="0.0" weightY="0.0"/>
+        </Constraint>
+      </Constraints>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
+      <SubComponents>
+        <Container class="javax.swing.JScrollPane" name="m_suiteScroll">
+          <Properties>
+            <Property name="autoscrolls" type="boolean" value="true"/>
+            <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[400, 147]"/>
+            </Property>
+            <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[400, 147]"/>
+            </Property>
+          </Properties>
+          <AuxValues>
+            <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+          </AuxValues>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="0" gridY="0" gridWidth="4" gridHeight="2" fill="1" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="3" insetsBottom="3" insetsRight="3" anchor="10" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JList" name="m_suiteList">
+              <Properties>
+                <Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+                  <Connection code="new TestListModel()" type="code"/>
+                </Property>
+                <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+                  <Dimension value="[250, 147]"/>
+                </Property>
+                <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+                  <Dimension value="[250, 147]"/>
+                </Property>
+                <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+                  <Dimension value="[250, 147]"/>
+                </Property>
+              </Properties>
+            </Component>
+          </SubComponents>
+        </Container>
+        <Component class="javax.swing.JButton" name="m_allButton">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Select All"/>
+            <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[90, 23]"/>
+            </Property>
+            <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[90, 23]"/>
+            </Property>
+            <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[90, 23]"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="m_allButtonActionPerformed"/>
+          </Events>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="4" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+        <Component class="javax.swing.JButton" name="m_executeButton">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="Execute"/>
+            <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[90, 23]"/>
+            </Property>
+            <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[90, 23]"/>
+            </Property>
+            <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[90, 23]"/>
+            </Property>
+          </Properties>
+          <Events>
+            <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="m_executeButtonActionPerformed"/>
+          </Events>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="4" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+        </Component>
+        <Container class="javax.swing.JScrollPane" name="m_resultScroll">
+          <Properties>
+            <Property name="verticalScrollBarPolicy" type="int" value="22"/>
+            <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+              <Dimension value="[452, 402]"/>
+            </Property>
+          </Properties>
+          <AuxValues>
+            <AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
+          </AuxValues>
+          <Constraints>
+            <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+              <GridBagConstraints gridX="0" gridY="2" gridWidth="5" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="3" insetsBottom="3" insetsRight="3" anchor="10" weightX="0.0" weightY="0.0"/>
+            </Constraint>
+          </Constraints>
+
+          <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
+          <SubComponents>
+            <Component class="javax.swing.JTable" name="m_resultTable">
+              <Properties>
+                <Property name="autoCreateRowSorter" type="boolean" value="true"/>
+                <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
+                  <Font name="Tahoma" size="10" style="0"/>
+                </Property>
+                <Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
+                  <Connection code="new ResultTableModel()" type="code"/>
+                </Property>
+                <Property name="autoResizeMode" type="int" value="4"/>
+                <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+                  <Dimension value="[300, 400]"/>
+                </Property>
+                <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+                  <Dimension value="[300, 400]"/>
+                </Property>
+                <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
+                  <Dimension value="null"/>
+                </Property>
+              </Properties>
+              <Events>
+                <EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="m_resultTableMouseClicked"/>
+              </Events>
+            </Component>
+          </SubComponents>
+        </Container>
+      </SubComponents>
+    </Container>
+    <Container class="javax.swing.JPanel" name="m_statusBar">
+      <Constraints>
+        <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
+          <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
+        </Constraint>
+      </Constraints>
+
+      <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
+      <SubComponents>
+        <Component class="javax.swing.JProgressBar" name="m_progress">
+          <Properties>
+            <Property name="indeterminate" type="boolean" value="true"/>
+          </Properties>
+        </Component>
+        <Component class="javax.swing.JLabel" name="m_executedResults">
+          <Properties>
+            <Property name="text" type="java.lang.String" value="aaaaaaaaaaaaaaa"/>
+          </Properties>
+        </Component>
+      </SubComponents>
+    </Container>
+  </SubComponents>
+</Form>
diff --git a/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/SwingRunner.java b/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/SwingRunner.java
new file mode 100644
index 0000000..82c4366
--- /dev/null
+++ b/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/SwingRunner.java
@@ -0,0 +1,475 @@
+/* 
+ * 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.ipojo.junit4osgi.command;
+
+import java.awt.Cursor;
+import java.awt.Point;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.swing.JScrollBar;
+
+import javax.swing.table.TableColumn;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import org.apache.felix.ipojo.junit4osgi.OSGiJunitRunner;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+
+/**
+ * Swing Runner for Junit4Osgi.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class SwingRunner extends javax.swing.JFrame implements BundleListener {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * Bundle context (to register the bundle listener).
+     */
+    private BundleContext m_context;
+    
+    /**
+     * OSGi Junit Runner Service. 
+     */
+    private OSGiJunitRunner m_runner;
+    
+    /**
+     * State variable describing if we are executing tests.
+     */
+    private boolean m_running = false;
+    
+    // Variables declaration - do not modify//GEN-BEGIN:variables
+    private javax.swing.JButton m_allButton;
+    private javax.swing.JButton m_executeButton;
+    private javax.swing.JLabel m_executedResults;
+    private javax.swing.JScrollPane m_message;
+    private javax.swing.JTextArea m_messageArea;
+    private javax.swing.JButton m_ok;
+    private javax.swing.JPanel m_panel;
+    private javax.swing.JProgressBar m_progress;
+    private javax.swing.JDialog m_resultDialog;
+    private javax.swing.JScrollPane m_resultScroll;
+    private javax.swing.JTable m_resultTable;
+    private javax.swing.JPanel m_statusBar;
+    private javax.swing.JList m_suiteList;
+    private javax.swing.JScrollPane m_suiteScroll;
+    // End of variables declaration//GEN-END:variables
+
+    /**
+     * Constructor.
+     * @param bc : bundle context.
+     */
+    public SwingRunner(BundleContext bc) {
+        m_context = bc;
+    }
+
+    /**
+     * Start method.
+     */
+    public void start() {
+        initComponents();
+        setVisible(true);
+        m_resultDialog.setVisible(false);
+        refreshSuites();
+        m_context.addBundleListener(this);
+        m_executedResults.setText(" \t No executed tests");
+        m_progress.setIndeterminate(false);
+        m_progress.setMaximum(100);
+        m_progress.setValue(100);
+        
+        TableColumn column = null;
+        for (int i = 0; i < m_resultTable.getColumnCount(); i++) {
+            column = m_resultTable.getColumnModel().getColumn(i);
+            if (i == 0) {
+                column.setPreferredWidth(350); //first column is bigger
+            } else {
+                column.setPreferredWidth(50);
+                column.setCellRenderer(new ResultCellRenderer());
+        }
+}
+    }
+
+    /**
+     * Stop method.
+     */
+    public void stop() {
+        m_context.removeBundleListener(this);
+        dispose();
+    }
+
+    /**
+     * Refresh the list of available test suites.
+     */
+    private void refreshSuites() {
+        List<Test> list = m_runner.getTests();
+        TestListModel lm = (TestListModel) m_suiteList.getModel();
+        lm.clear();
+        for (Test t : list) {
+            lm.addTest(t);
+        }
+    }
+
+    /** This method is called from within the constructor to
+     * initialize the form.
+     * WARNING: Do NOT modify this code. The content of this method is
+     * always regenerated by the Form Editor.
+     */
+    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
+    private void initComponents() {
+        java.awt.GridBagConstraints gridBagConstraints;
+
+        m_resultDialog = new javax.swing.JDialog();
+        m_message = new javax.swing.JScrollPane();
+        m_messageArea = new javax.swing.JTextArea();
+        m_ok = new javax.swing.JButton();
+        m_panel = new javax.swing.JPanel();
+        m_suiteScroll = new javax.swing.JScrollPane();
+        m_suiteList = new javax.swing.JList();
+        m_allButton = new javax.swing.JButton();
+        m_executeButton = new javax.swing.JButton();
+        m_resultScroll = new javax.swing.JScrollPane();
+        m_resultTable = new javax.swing.JTable();
+        m_statusBar = new javax.swing.JPanel();
+        m_progress = new javax.swing.JProgressBar();
+        m_executedResults = new javax.swing.JLabel();
+
+        m_resultDialog.setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
+        m_resultDialog.setMinimumSize(new java.awt.Dimension(320, 250));
+        m_resultDialog.addWindowListener(new java.awt.event.WindowAdapter() {
+            public void windowClosed(java.awt.event.WindowEvent evt) {
+                onDialogClosed(evt);
+            }
+            public void windowClosing(java.awt.event.WindowEvent evt) {
+                onDialogClosed(evt);
+            }
+        });
+
+        m_message.setBorder(null);
+        m_message.setMinimumSize(new java.awt.Dimension(300, 202));
+        m_message.setPreferredSize(new java.awt.Dimension(300, 202));
+
+        m_messageArea.setBackground(javax.swing.UIManager.getDefaults().getColor("Panel.background"));
+        m_messageArea.setColumns(20);
+        m_messageArea.setEditable(false);
+        m_messageArea.setLineWrap(true);
+        m_messageArea.setRows(5);
+        m_messageArea.setWrapStyleWord(true);
+        m_messageArea.setMinimumSize(new java.awt.Dimension(300, 250));
+        m_messageArea.setPreferredSize(new java.awt.Dimension(250, 200));
+        m_message.setViewportView(m_messageArea);
+
+        m_resultDialog.getContentPane().add(m_message, java.awt.BorderLayout.CENTER);
+
+        m_ok.setText("Ok");
+        m_ok.setPreferredSize(new java.awt.Dimension(120, 23));
+        m_ok.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                okActionPerformed(evt);
+            }
+        });
+        m_resultDialog.getContentPane().add(m_ok, java.awt.BorderLayout.SOUTH);
+
+        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
+        setTitle("Junit Runner");
+        setMinimumSize(new java.awt.Dimension(580, 580));
+        getContentPane().setLayout(new java.awt.GridBagLayout());
+
+        m_panel.setMinimumSize(new java.awt.Dimension(580, 560));
+        m_panel.setPreferredSize(new java.awt.Dimension(580, 566));
+        m_panel.setLayout(new java.awt.GridBagLayout());
+
+        m_suiteScroll.setAutoscrolls(true);
+        m_suiteScroll.setMinimumSize(new java.awt.Dimension(400, 147));
+        m_suiteScroll.setPreferredSize(new java.awt.Dimension(400, 147));
+
+        m_suiteList.setModel(new TestListModel());
+        m_suiteList.setMaximumSize(new java.awt.Dimension(250, 147));
+        m_suiteList.setMinimumSize(new java.awt.Dimension(250, 147));
+        m_suiteList.setPreferredSize(new java.awt.Dimension(250, 147));
+        m_suiteScroll.setViewportView(m_suiteList);
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.gridwidth = 4;
+        gridBagConstraints.gridheight = 2;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+        gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
+        m_panel.add(m_suiteScroll, gridBagConstraints);
+
+        m_allButton.setText("Select All");
+        m_allButton.setMaximumSize(new java.awt.Dimension(90, 23));
+        m_allButton.setMinimumSize(new java.awt.Dimension(90, 23));
+        m_allButton.setPreferredSize(new java.awt.Dimension(90, 23));
+        m_allButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                allButtonActionPerformed(evt);
+            }
+        });
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 4;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
+        gridBagConstraints.insets = new java.awt.Insets(3, 0, 0, 0);
+        m_panel.add(m_allButton, gridBagConstraints);
+
+        m_executeButton.setText("Execute");
+        m_executeButton.setMaximumSize(new java.awt.Dimension(90, 23));
+        m_executeButton.setMinimumSize(new java.awt.Dimension(90, 23));
+        m_executeButton.setPreferredSize(new java.awt.Dimension(90, 23));
+        m_executeButton.addActionListener(new java.awt.event.ActionListener() {
+            public void actionPerformed(java.awt.event.ActionEvent evt) {
+                executeButtonActionPerformed(evt);
+            }
+        });
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 4;
+        gridBagConstraints.gridy = 1;
+        gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
+        gridBagConstraints.insets = new java.awt.Insets(2, 0, 0, 0);
+        m_panel.add(m_executeButton, gridBagConstraints);
+
+        m_resultScroll.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
+        m_resultScroll.setMinimumSize(new java.awt.Dimension(452, 402));
+
+        m_resultTable.setFont(new java.awt.Font("Tahoma", 0, 10));
+        m_resultTable.setModel(new ResultTableModel());
+        m_resultTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_ALL_COLUMNS);
+        m_resultTable.setMaximumSize(new java.awt.Dimension(300, 400));
+        m_resultTable.setMinimumSize(new java.awt.Dimension(300, 400));
+        m_resultTable.setPreferredSize(null);
+        m_resultTable.addMouseListener(new java.awt.event.MouseAdapter() {
+            public void mouseClicked(java.awt.event.MouseEvent evt) {
+                resultTableMouseClicked(evt);
+            }
+        });
+        m_resultScroll.setViewportView(m_resultTable);
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 2;
+        gridBagConstraints.gridwidth = 5;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+        gridBagConstraints.insets = new java.awt.Insets(2, 3, 3, 3);
+        m_panel.add(m_resultScroll, gridBagConstraints);
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 0;
+        gridBagConstraints.gridheight = 2;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
+        gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
+        getContentPane().add(m_panel, gridBagConstraints);
+
+        m_statusBar.setLayout(new javax.swing.BoxLayout(m_statusBar, javax.swing.BoxLayout.LINE_AXIS));
+
+        m_progress.setIndeterminate(true);
+        m_statusBar.add(m_progress);
+
+        m_executedResults.setText("aaaaaaaaaaaaaaa");
+        m_statusBar.add(m_executedResults);
+
+        gridBagConstraints = new java.awt.GridBagConstraints();
+        gridBagConstraints.gridx = 0;
+        gridBagConstraints.gridy = 2;
+        gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
+        getContentPane().add(m_statusBar, gridBagConstraints);
+
+        pack();
+    }// </editor-fold>//GEN-END:initComponents
+
+    /**
+     * Execute button action.
+     * @param evt : event.
+     */
+    private void executeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_m_executeButtonActionPerformed
+        if (m_running) { return; }
+        // Collect selected test.
+        int[] indices = m_suiteList.getSelectedIndices();
+        List<Test> list = new ArrayList<Test>(indices.length);
+        TestListModel model = (TestListModel) m_suiteList.getModel();
+        for (int i = 0; i < indices.length; i++) {
+            list.add(model.getTestElementAt(indices[i]));
+        }
+        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
+        executeTest(list);
+    }//GEN-LAST:event_m_executeButtonActionPerformed
+
+    /**
+     * All button action.
+     * @param evt : event.
+     */
+    private void allButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_m_allButtonActionPerformed
+        int max = m_suiteList.getModel().getSize();
+        int[] indices = new int[max];
+        for (int i = 0; i < max; i++) {
+            indices[i] = i;
+        }
+        m_suiteList.setSelectedIndices(indices);
+    }//GEN-LAST:event_m_allButtonActionPerformed
+
+    /**
+     * Listener on table click.
+     * @param evt : event.
+     */
+    private void resultTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_m_resultTableMouseClicked
+        Point p = evt.getPoint();
+        int row = m_resultTable.rowAtPoint(p);
+        int col = m_resultTable.columnAtPoint(p);
+        ResultTableModel model = (ResultTableModel) m_resultTable.getModel();
+        String message = model.getMessage(row, col);
+        if (message != null) {
+            setEnabled(false);
+            m_resultDialog.setTitle("Test Report");
+            m_messageArea.setText(message);
+            m_resultDialog.setVisible(true);
+        }
+    }//GEN-LAST:event_m_resultTableMouseClicked
+
+    /**
+     * Ok button action.
+     * @param evt : event.
+     */
+    private void okActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_m_okActionPerformed
+        m_resultDialog.setVisible(false);
+        setEnabled(true);
+    }//GEN-LAST:event_m_okActionPerformed
+
+    /**
+     * Listener when the test report is closed.
+     * @param evt : event.
+     */
+    private void onDialogClosed(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_onDialogClosed
+        m_resultDialog.setVisible(false);
+        setEnabled(true);
+    }//GEN-LAST:event_onDialogClosed
+
+    /**
+     * Execute method.
+     * @param list : list of test to execute.
+     */
+    private void executeTest(final List<Test> list) {
+        Runnable thread = new Runnable() {
+            public void run() {
+                ResultTableModel model = (ResultTableModel) m_resultTable.getModel();
+                m_running = true;
+                m_executeButton.setText("Running...");
+                m_progress.setIndeterminate(true);
+                model.clear();
+                for (int i = 0; i < list.size(); i++) {
+                    TestResult tr = new TestResult();
+                    tr.addListener(new MyTestListener());
+                    list.get(i).run(tr);
+                }
+                m_running = false;
+                m_progress.setIndeterminate(false);
+                m_progress.setMaximum(100);
+                m_progress.setValue(100);
+                m_executeButton.setText("Execute");
+                computeExecutedTest();
+                setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
+            }
+
+        };
+        new Thread(thread).start();
+    }
+    
+     private void computeExecutedTest() {
+        ResultTableModel results = (ResultTableModel) m_resultTable.getModel();
+        String m = " \t ";
+        m+= results.getTestCount() + " tests executed / ";
+        m+= results.getSucess() + " sucess / ";
+        m+= results.getFailures() + " failures / ";
+        m+= results.getErrors() + " errors ";
+        m_executedResults.setText(m);
+     }
+
+    private class MyTestListener implements junit.framework.TestListener {
+        /**
+         * Table model.
+         */
+        ResultTableModel m_model = (ResultTableModel) m_resultTable.getModel();
+
+        /**
+         * Add an error.
+         * @param arg0 : test which throws an error.
+         * @param arg1 : thrown exception.
+         * @see junit.framework.TestListener#addError(junit.framework.Test, java.lang.Throwable)
+         */
+        public void addError(Test arg0, Throwable arg1) {
+            m_model.addTest(arg0, arg1);
+            adjustScroll();
+        }
+
+        /**
+         * Add a failure.
+         * @param arg0 : failing test.
+         * @param arg1 : thrown failure.
+         * @see junit.framework.TestListener#addError(junit.framework.Test, java.lang.Throwable)
+         */
+        public void addFailure(Test arg0, AssertionFailedError arg1) {
+            m_model.addTest(arg0, arg1);
+            adjustScroll();
+        }
+
+        /**
+         * End of a test.
+         * @param arg0 : test.
+         * @see junit.framework.TestListener#endTest(junit.framework.Test)
+         */
+        public void endTest(Test arg0) {
+            m_model.addTest(arg0);
+            adjustScroll();
+        }
+
+        /**
+         * Start of a test.
+         * @param arg0 : test.
+         * @see junit.framework.TestListener#startTest(junit.framework.Test)
+         */
+        public void startTest(Test arg0) {
+            // Nothing to do here.
+        }
+
+        /**
+         * Adjust the scrolling bar of the result table.
+         */
+        private void adjustScroll() {
+            JScrollBar bar = m_resultScroll.getVerticalScrollBar();
+            if ((bar != null) && (bar.isVisible())) {
+                bar.setValue(Integer.MAX_VALUE);
+            }
+        }
+
+    }
+
+    /**
+     * Bundle Changed callback method.
+     * @param arg0 : bundle event.
+     * @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent)
+     */
+    public void bundleChanged(BundleEvent arg0) {
+        refreshSuites();
+    }
+
+}
diff --git a/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/TestListModel.java b/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/TestListModel.java
new file mode 100644
index 0000000..4811c5e
--- /dev/null
+++ b/ipojo/examples/junit4osgi/swing-runner/src/main/java/org/apache/felix/ipojo/junit4osgi/command/TestListModel.java
@@ -0,0 +1,89 @@
+/* 
+ * 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.ipojo.junit4osgi.command;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.swing.AbstractListModel;
+import junit.framework.Test;
+
+/**
+ * Test Suite list model.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class TestListModel extends AbstractListModel {
+
+    private static final long serialVersionUID = 1L;
+
+    private List<TestRecord> list = new ArrayList<TestRecord>();
+
+    public Object getElementAt(int index) {
+        if (index >= list.size()) {
+            return null;
+        } else {
+            return list.get(index).name;
+        }
+    }
+
+    public Test getTestElementAt(int index) {
+        return list.get(index).test;
+    }
+
+    public void addTest(Test test) {
+        synchronized(this) {
+            TestRecord tr = new TestRecord();
+            tr.test = test;
+            tr.name = test.toString();
+            list.add(tr);
+        }
+        fireContentsChanged(this, list.size() - 1, list.size() - 1);
+    }
+
+    public void removeTest(Test test) {
+        int index = 1;
+        synchronized(this) {
+            for (TestRecord t : list) {
+                if (t.test.equals(test)) {
+                    index = list.indexOf(t);
+                    list.remove(t);
+                    return;
+                }
+            }
+        }
+        
+        if (index != -1) {
+            fireContentsChanged(this, index, index);
+        }
+    }
+
+    public void clear() {
+        list.clear();
+    }
+
+    private class TestRecord {
+        public Test test;
+
+        public String name;
+    }
+
+    public int getSize() {
+        return list.size();
+    }
+
+}