Move the junit4osgi maven plugin to the trunk
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@720005 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/pom.xml b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/pom.xml
new file mode 100644
index 0000000..dafd5e5
--- /dev/null
+++ b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/pom.xml
@@ -0,0 +1,83 @@
+<!--
+ 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 xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.apache.felix.ipojo.junit4osgi</groupId>
+ <artifactId>maven-junit4osgi-plugin</artifactId>
+ <packaging>maven-plugin</packaging>
+ <version>1.1.0-SNAPSHOT</version>
+ <name>maven-junit4osgi-plugin Maven Mojo</name>
+ <url>http://maven.apache.org</url>
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-plugin-api</artifactId>
+ <version>2.0</version>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>3.8.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.framework</artifactId>
+ <version>1.4.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-artifact</artifactId>
+ <version>2.0.7</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-project</artifactId>
+ <version>2.0.7</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-model</artifactId>
+ <version>2.0.7</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.maven</groupId>
+ <artifactId>maven-plugin-api</artifactId>
+ <version>2.0.7</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo</artifactId>
+ <version>1.1.0-SNAPSHOT</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.ipojo.handler.extender</artifactId>
+ <version>1.1.0-SNAPSHOT</version>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>ipojo.examples</groupId>
+ <artifactId>org.apache.felix.ipojo.junit4osgi</artifactId>
+ <version>1.1.0-SNAPSHOT</version>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Installer.java b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Installer.java
new file mode 100644
index 0000000..a7a8078
--- /dev/null
+++ b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Installer.java
@@ -0,0 +1,196 @@
+/*
+ * 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.plugin;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.List;
+
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.project.MavenProject;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+
+/**
+ * Bundle Activator installing bundles in the embedded OSGi.
+ * Installed bundles are the junit4osgi framework, the required bundle and the artifact bundle (if enable).
+ * Bundles are installed from the local maven repository.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Installer implements BundleActivator {
+
+ /**
+ * The list of artifact containing bundles for the junit4osgi framework.
+ */
+ private List artifacts;
+
+ /**
+ * The current maven project.
+ */
+ private MavenProject project;
+
+ /**
+ * Flag enabling/disbling the deployment of the current
+ * project artifact.
+ */
+ private boolean deployCurrent;
+
+ /**
+ * List of bundle URLs to install.
+ */
+ private List bundles;
+
+
+ /**
+ * Creates a Installer
+ * @param artifacts the list of artifact containing bundles for the junit4osgi framework.
+ * @param bundles the list of bundle URLs to install
+ * @param project the current maven project
+ * @param deployCurrentArtifact flag enabling/disabling the deployment of the current project artifact
+ */
+ public Installer(List artifacts, List bundles, MavenProject project, boolean deployCurrentArtifact) {
+ this.artifacts = artifacts;
+ this.project = project;
+ deployCurrent = deployCurrentArtifact;
+ this.bundles = bundles;
+ }
+
+ /**
+ * Installs and starts the iPOJO bundle.
+ * @param context the bundle context.
+ * @throws BundleException when the bundle cannot be installed or started correctly
+ */
+ private void installIPOJO(BundleContext context) throws BundleException {
+ String path = getUrlByArtifactId("org.apache.felix.ipojo").toString();
+ Bundle bundle = context.installBundle(path);
+ bundle.start();
+ }
+
+ /**
+ * Installs and starts the Junit4OSGi bundle.
+ * @param context the bundle context.
+ * @throws BundleException when the bundle cannot be installed or started correctly
+ */
+ private void installJunit(BundleContext context) throws BundleException {
+ String path = getUrlByArtifactId("org.apache.felix.ipojo.junit4osgi").toString();
+ Bundle bundle = context.installBundle(path);
+ bundle.start();
+ }
+
+ /**
+ * Installs and starts the iPOJO Extender Handler bundle.
+ * @param context the bundle context.
+ * @throws BundleException when the bundle cannot be installed or started correctly
+ */
+ private void installExtender(BundleContext context) throws BundleException {
+ String path = getUrlByArtifactId("org.apache.felix.ipojo.handler.extender").toString();
+ Bundle bundle = context.installBundle(path);
+ bundle.start();
+ }
+
+ /**
+ * Installs and Starts required bundles.
+ * @throws BundleException when a bundle cannot be installed or started correctly
+ */
+ private void deployBundles(BundleContext context) throws BundleException {
+ for (int i = 0; i < bundles.size(); i++) {
+ URL url = (URL) bundles.get(i);
+ Bundle bundle = context.installBundle(url.toString());
+ bundle.start();
+ }
+ }
+
+ /**
+ * Gets the bundle URL from the artifact list.
+ * @param id the dependency id.
+ * @return the bundle URL or <code>null</code> if the URL cannot
+ * be found.
+ */
+ private URL getUrlByArtifactId(String id) {
+ for (int i = 0; i < artifacts.size(); i++) {
+ Artifact artifact = (Artifact) artifacts.get(i);
+ if (artifact.getArtifactId().equals(id)) {
+ try {
+ return artifact.getFile().toURL();
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Deploys the current bundle if enable.
+ * @param context the bundle context
+ * @throws BundleException when the bundle cannot be installed or started correctly.
+ */
+ private void deployProjectArtifact(BundleContext context)
+ throws BundleException {
+ if (!deployCurrent) {
+ return;
+ }
+
+ File file = project.getArtifact().getFile();
+ if (file.exists()) {
+ URL url = null;
+ try {
+ url = file.toURL();
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ Bundle bundle = context.installBundle(url.toString());
+ bundle.start();
+ } else {
+ throw new BundleException("The current project artifact does not exist (" + file.getAbsolutePath() + ")");
+ }
+ }
+
+
+ /**
+ * Start bundles.
+ * @param context the bundle context.
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext context) {
+ try {
+ installIPOJO(context);
+ installExtender(context);
+ installJunit(context);
+ deployBundles(context);
+ deployProjectArtifact(context);
+ } catch (BundleException e) {
+ System.err.println("Cannot start the framework : " + e.getMessage());
+ return;
+ }
+ }
+
+ /**
+ * Stopping methods.
+ * @param context the bundle context.
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext context) {
+ // Do nothing.
+ }
+
+}
\ No newline at end of file
diff --git a/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Junit4osgiPlugin.java b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Junit4osgiPlugin.java
new file mode 100644
index 0000000..6af90e8
--- /dev/null
+++ b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Junit4osgiPlugin.java
@@ -0,0 +1,461 @@
+/*
+ * 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.plugin;/*
+
+ *
+ * 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.
+ */
+
+import java.io.File;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.jar.JarFile;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestFailure;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+
+import org.apache.felix.framework.Felix;
+import org.apache.felix.ipojo.junit4osgi.plugin.log.LogServiceImpl;
+import org.apache.maven.artifact.Artifact;
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.project.MavenProject;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Goal starting Felix and executing junit4osgi tests.
+ *
+ * @goal test
+ * @phase integration-test
+ * @requiresDependencyResolution runtime
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ *
+ */
+public class Junit4osgiPlugin extends AbstractMojo {
+
+ /**
+ * The Maven project.
+ *
+ * @parameter expression="${project}"
+ * @required
+ * @readonly
+ */
+ private MavenProject project;
+
+ /** @parameter expression="${plugin.artifacts}" */
+ private java.util.List pluginArtifacts;
+
+ /**
+ * Base directory where all reports are written to.
+ *
+ * @parameter expression="${project.build.directory}/junit4osgi-reports"
+ */
+ private File reportsDirectory;
+
+ /**
+ * Base directory where all reports are written to.
+ *
+ * @parameter expression="${project.build.directory}"
+ */
+ private File targetDir;
+
+ /**
+ * Must the current artifact be deployed?.
+ *
+ * @parameter expression="${deployProjectArtifact}" default-value="false"
+ */
+ private boolean deployProjectArtifact;
+
+ /**
+ * Required bundles
+ *
+ * @parameter expression="${bundles}"
+ */
+ private ArrayList bundles;
+
+ int total;
+ int totalFailures;
+ int totalErrors;
+
+ List errors = new ArrayList();
+ List failures = new ArrayList();
+ List results = new ArrayList();
+
+ /**
+ * Log Service exposed by the plugin framework.
+ */
+ private LogServiceImpl logService;
+
+
+ public void execute() throws MojoFailureException {
+
+ List bundles = parseBundleList();
+ bundles.addAll(getTestBundle());
+
+ List activators = new ArrayList();
+ logService = new LogServiceImpl();
+ activators.add(logService);
+ activators.add(new Installer(pluginArtifacts, bundles, project, deployProjectArtifact));
+ Map map = new HashMap();
+ map.put("felix.systembundle.activators", activators);
+ map.put("org.osgi.framework.storage.clean", "onFirstInit");
+ map.put("ipojo.log.level", "WARNING");
+ map.put("org.osgi.framework.bootdelegation", "junit.framework, org.osgi.service.log");
+ map.put("org.osgi.framework.storage", targetDir.getAbsolutePath() + "/felix-cache");
+
+
+ System.out.println("");
+ System.out.println("-------------------------------------------------------");
+ System.out.println(" T E S T S");
+ System.out.println("-------------------------------------------------------");
+
+ Felix felix = new Felix(map);
+ try {
+ felix.start();
+ } catch (BundleException e) {
+ e.printStackTrace();
+ }
+
+ waitForStability(felix.getBundleContext());
+
+
+ Object runner = waitForRunnerService(felix.getBundleContext());
+ invokeRun(runner, felix.getBundleContext());
+
+ try {
+ felix.stop();
+ felix.waitForStop(5000);
+ } catch (Exception e) {
+ getLog().error(e);
+ }
+
+ if (totalErrors > 0 || totalFailures > 0) {
+ throw new MojoFailureException("There are test failures. \n\n" +
+ "Please refer to " + reportsDirectory.getAbsolutePath() +
+ " for the individual test results.");
+ }
+
+ }
+
+ private void waitForStability(BundleContext context) throws MojoFailureException {
+ // Wait for bundle initialization.
+ boolean bundleStability = getBundleStability(context);
+ int count = 0;
+ while(!bundleStability && count < 500) {
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException e) {
+ // Interrupted
+ }
+ count++;
+ bundleStability = getBundleStability(context);
+ }
+
+ if (count == 500) {
+ getLog().error("Bundle stability isn't reached after 500 tries");
+ dumpBundles(context);
+ throw new MojoFailureException("Cannot reach the bundle stability");
+ }
+
+ boolean serviceStability = false;
+ count = 0;
+ int count1 = 0, count2 = 0;
+ while(! serviceStability && count < 500) {
+ try {
+ ServiceReference[] refs = context.getServiceReferences(null, null);
+ count1 = refs.length;
+ Thread.sleep(500);
+ refs = context.getServiceReferences(null, null);
+ count2 = refs.length;
+ serviceStability = (count1 == count2);
+ } catch (Exception e) {
+ getLog().error(e);
+ serviceStability = false;
+ // Nothing to do, while recheck the condition
+ }
+ count++;
+ }
+
+ if (count == 500) {
+ getLog().error("Service stability isn't reached after 500 tries (" + count1 + " != " + count2);
+ dumpBundles(context);
+ throw new MojoFailureException("Cannot reach the service stability");
+ }
+
+ }
+
+ private boolean getBundleStability(BundleContext bc) {
+ boolean stability = true;
+ Bundle[] bundles = bc.getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ stability = stability && (bundles[i].getState() == Bundle.ACTIVE);
+ }
+ return stability;
+ }
+
+ private List parseBundleList() {
+ List toDeploy = new ArrayList();
+ if (bundles == null) {
+ return toDeploy;
+ }
+
+ for (int i = 0; i < bundles.size(); i++) {
+ String bundle = (String) bundles.get(i);
+ try {
+ URL url = new URL(bundle);
+ toDeploy.add(url);
+ } catch (MalformedURLException e) {
+ // Not a valid url,
+ getLog().error(bundle + " is not a valid url, bundle ignored");
+
+ }
+ }
+
+ return toDeploy;
+ }
+
+ private List getTestBundle() {
+ List toDeploy = new ArrayList();
+ Set dependencies = project.getDependencyArtifacts();
+ for (Iterator artifactIterator = dependencies.iterator(); artifactIterator.hasNext();) {
+ Artifact artifact = (Artifact) artifactIterator.next();
+ if (Artifact.SCOPE_TEST.equals(artifact.getScope())) { // Select scope=test.
+ File file = artifact.getFile();
+ try {
+ JarFile jar = new JarFile(file);
+ if (jar.getManifest().getMainAttributes().getValue("Bundle-ManifestVersion") != null) {
+ toDeploy.add(file.toURL());
+ }
+ } catch (Exception e) {
+ getLog().error(e);
+ }
+ }
+ }
+ return toDeploy;
+ }
+
+ private Object waitForRunnerService(BundleContext bc) {
+ ServiceReference ref = bc.getServiceReference(org.apache.felix.ipojo.junit4osgi.OSGiJunitRunner.class.getName());
+ int count = 0;
+ while (ref == null && count < 1000) {
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException e) {
+ // Nothing to do
+ }
+ ref = bc.getServiceReference(org.apache.felix.ipojo.junit4osgi.OSGiJunitRunner.class.getName());
+ }
+ if (ref != null) {
+ return bc.getService(ref);
+ }
+ getLog().error("Junit Runner service unavailable");
+ return null;
+ }
+
+ private void invokeRun(Object runner, BundleContext bc) {
+ Method getTest;
+
+ try {
+ getTest = runner.getClass().getMethod("getTests", new Class[0]);
+ List tests = (List) getTest.invoke(runner, new Object[0]);
+ Method run = getRunMethod(runner);
+ for (int i = 0; i < tests.size(); i++) {
+ executeTest(runner, (Test) tests.get(i),run, bc);
+ }
+ System.out.println("\nResults :");
+ if (failures.size() > 0) {
+ System.out.println("\nFailed tests:");
+ for (int i = 0; i < failures.size(); i++) {
+ TestResult tr = (TestResult) failures.get(i);
+ Enumeration e = tr.failures();
+ while(e.hasMoreElements()) {
+ TestFailure tf = (TestFailure) e.nextElement();
+ System.out.println(" " + tf.toString());
+ }
+ }
+ }
+
+ if (failures.size() > 0) {
+ System.out.println("\nTests in error:");
+ for (int i = 0; i < errors.size(); i++) {
+ TestResult tr = (TestResult) errors.get(i);
+ Enumeration e = tr.errors();
+ while(e.hasMoreElements()) {
+ TestFailure tf = (TestFailure) e.nextElement();
+ System.out.println(" " + tf.toString());
+ }
+ }
+ }
+
+ System.out.println("\nTests run: " + total + ", Failures: " + totalFailures + ", Errors:" + totalErrors + "\n");
+ } catch (Exception e) {
+ getLog().error(e);
+ }
+ }
+
+ private Method getRunMethod(Object runner) {
+ Method[] methods = runner.getClass().getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ if (methods[i].getName().equals("run")
+ && methods[i].getParameterTypes().length == 1
+ && ! methods[i].getParameterTypes()[0].equals(Long.TYPE)) {
+ return methods[i];
+ }
+ }
+ getLog().error("Cannot find the run method");
+ return null;
+ }
+
+ private String getTestName(Object test) {
+ try {
+ Method getName = test.getClass().getMethod("getName", new Class[0]);
+ String name = (String) getName.invoke(test, new Object[0]);
+ if (name == null) {
+ name = test.toString();
+ }
+ return name;
+ } catch (Exception e) {
+ getLog().error(e);
+ return null;
+ }
+
+ }
+
+ private void executeTest(Object runner, Test test, Method run, BundleContext bc) {
+ try {
+ XMLReport report = new XMLReport();
+ String name = getTestName(test);
+ System.out.println("Running " + name);
+
+ TestResult tr = new TestResult();
+ tr.addListener(new ResultListener(report));
+ test.run(tr);
+ results.add(tr);
+
+ if (tr.wasSuccessful()) {
+ System.out.println("Tests run: " + tr.runCount() + ", Failures: " + tr.failureCount() + ", Errors: " + tr.errorCount() + ", Time elapsed: " + report.elapsedTimeAsString(report.endTime - report.endTime) + " sec");
+ } else {
+ System.out.println("Tests run: " + tr.runCount() + ", Failures: " + tr.failureCount() + ", Errors: " + tr.errorCount() + ", Time elapsed: " + report.elapsedTimeAsString(report.endTime - report.endTime) + " sec <<< FAILURE!");
+ if (tr.errorCount() > 0) {
+ errors.add(tr);
+ }
+ if (tr.failureCount() > 0) {
+ failures.add(tr);
+ }
+ }
+
+ total += tr.runCount();
+ totalFailures += tr.failureCount();
+ totalErrors += tr.errorCount();
+
+ report.generateReport(test, tr, reportsDirectory, bc);
+
+ } catch (Exception e) {
+ getLog().error(e);
+ }
+
+
+
+ }
+
+ public void dumpBundles(BundleContext bc) {
+ getLog().info("Bundles:");
+ Bundle[] bundles = bc.getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ getLog().info(bundles[i].getSymbolicName() + " - " + bundles[i].getState());
+ }
+ }
+
+ public LogServiceImpl getLogService() {
+ return logService;
+ }
+
+
+ private class ResultListener implements TestListener {
+
+ private XMLReport report;
+ private boolean abort;
+
+ private PrintStream outBackup = System.out;
+ private PrintStream errBackup = System.err;
+
+ private StringOutputStream out = new StringOutputStream();
+ private StringOutputStream err = new StringOutputStream();;
+
+ public ResultListener(XMLReport report) {
+ this.report = report;
+ }
+
+ public void addError(Test test, Throwable throwable) {
+ report.testError(test, throwable, out.toString(), err.toString(), getLogService().getLoggedMessages());
+ abort = true;
+ }
+
+ public void addFailure(Test test,
+ AssertionFailedError assertionfailederror) {
+ report.testFailed(test, assertionfailederror, out.toString(), err.toString(), getLogService().getLoggedMessages());
+ abort = true;
+
+ }
+
+ public void endTest(Test test) {
+ if (!abort) {
+ report.testSucceeded(test);
+ }
+ System.setErr(errBackup);
+ System.setOut(outBackup);
+ getLogService().reset();
+ }
+
+ public void startTest(Test test) {
+ abort = false;
+ report.testStarting();
+ System.setErr(new PrintStream(err));
+ System.setOut(new PrintStream(out));
+ getLogService().enableOutputStream();
+ }
+
+ }
+
+}
diff --git a/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Report.java b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Report.java
new file mode 100644
index 0000000..cbb8905
--- /dev/null
+++ b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/Report.java
@@ -0,0 +1,238 @@
+/*
+ * 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.plugin;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import junit.framework.Test;
+
+import org.codehaus.plexus.util.StringUtils;
+
+/**
+ * Test report.
+ * This class is provides the basics to support several output format.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class Report {
+
+ /**
+ * Number of ran tests.
+ */
+ protected int completedCount;
+
+ /**
+ * Number of errors
+ */
+ protected int errorsCount;
+
+ /**
+ * Number of failures
+ */
+ protected int failuresCount;
+
+
+ /**
+ * Failing tests.
+ */
+ private List failureSources = new ArrayList();
+
+ /**
+ * Tests in error
+ */
+ private List errorSources = new ArrayList();
+
+ /**
+ * Time at the beginning of the test execution.
+ */
+ protected long startTime;
+
+ /**
+ * Time at the end of the test execution.
+ */
+ protected long endTime;
+
+ /**
+ * Double format.
+ */
+ private NumberFormat numberFormat = NumberFormat.getInstance( Locale.US );
+
+ /**
+ * New line constant.
+ */
+ protected static final String NL = System.getProperty( "line.separator" );
+
+ /**
+ * Gets failing tests.
+ * @return the list of failing tests.
+ */
+ public List getFailureSources() {
+ return this.failureSources;
+ }
+
+ /**
+ * Gets tests in error.
+ * @return the list of test throwing unexpected exceptions
+ */
+ public List getErrorSources() {
+ return this.errorSources;
+ }
+
+ /**
+ * Callback called when a test starts.
+ */
+ public void testStarting() {
+ startTime = System.currentTimeMillis();
+ }
+
+ /**
+ * Callback called when a test ends successfully.
+ */
+ public void testSucceeded() {
+ endTest();
+ }
+
+ /**
+ * Callback called when a test throws an unexpected error.
+ * @param test the test in error.
+ */
+ public void testError(Test test) {
+ ++errorsCount;
+ errorSources.add(test.toString());
+ endTest();
+ }
+
+ /**
+ * Callback called when a test fails.
+ * @param test the failing test.
+ */
+ public void testFailed(Test test) {
+ ++failuresCount;
+ failureSources.add(test.toString());
+ endTest();
+ }
+
+ /**
+ * Callback called when a test ends.
+ * This method handles common action when a test ends.
+ */
+ private void endTest() {
+ ++completedCount;
+
+ endTime = System.currentTimeMillis();
+
+ if (startTime == 0) {
+ startTime = endTime;
+ }
+ }
+
+
+ public int getNumErrors() {
+ return errorsCount;
+ }
+
+ public int getNumFailures() {
+ return failuresCount;
+ }
+
+ public int getNumTests() {
+ return completedCount;
+ }
+
+ /**
+ * Reset the report.
+ */
+ public void reset() {
+ errorsCount = 0;
+
+ failuresCount = 0;
+
+ completedCount = 0;
+
+ this.failureSources = new ArrayList();
+
+ this.errorSources = new ArrayList();
+
+ }
+
+
+ /**
+ * Returns the formatted String to display the given double.
+ * @param runTime the elapsed time
+ * @return the String displaying the elapsed time
+ */
+ protected String elapsedTimeAsString(long runTime) {
+ return numberFormat.format((double) runTime / 1000);
+ }
+
+ /**
+ * Returns the stacktrace as String.
+ * @param report ReportEntry object.
+ * @return stacktrace as string.
+ */
+ protected String getStackTrace(Test test, Throwable e) {
+
+ if (e == null) {
+ return "";
+ }
+
+ StringWriter w = new StringWriter();
+ if (e != null) {
+ e.printStackTrace(new PrintWriter(w));
+ w.flush();
+ }
+ String text = w.toString();
+ String marker = "at " + test.toString();
+
+ String[] lines = StringUtils.split(text, "\n");
+ int lastLine = lines.length - 1;
+ int causedByLine = -1;
+ // skip first
+ for (int i = 1; i < lines.length; i++) {
+ String line = lines[i].trim();
+ if (line.startsWith(marker)) {
+ lastLine = i;
+ } else if (line.startsWith("Caused by")) {
+ causedByLine = i;
+ break;
+ }
+ }
+
+ StringBuffer trace = new StringBuffer();
+ for (int i = 0; i <= lastLine; i++) {
+ trace.append(lines[i]);
+ trace.append("\n");
+ }
+
+ if (causedByLine != -1) {
+ for (int i = causedByLine; i < lines.length; i++) {
+ trace.append(lines[i]);
+ trace.append("\n");
+ }
+ }
+ return trace.toString();
+ }
+
+
+
+}
diff --git a/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/StringOutputStream.java b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/StringOutputStream.java
new file mode 100644
index 0000000..60186cf
--- /dev/null
+++ b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/StringOutputStream.java
@@ -0,0 +1,99 @@
+package org.apache.felix.ipojo.junit4osgi.plugin;
+
+import java.io.OutputStream;
+import java.io.Serializable;
+
+/**
+ * Provides an OutputStream to an internal String. Internally converts bytes to
+ * a Strings and stores them in an internal StringBuffer.
+ */
+public class StringOutputStream extends OutputStream implements Serializable {
+
+ /**
+ * Id.
+ */
+ private static final long serialVersionUID = -5912060965986156224L;
+
+ /**
+ * The internal destination StringBuffer.
+ */
+ protected StringBuffer buf = null;
+
+ /**
+ * Creates new StringOutputStream, makes a new internal StringBuffer.
+ */
+ public StringOutputStream() {
+ super();
+ buf = new StringBuffer();
+ }
+
+ /**
+ * Returns the content of the internal StringBuffer as a String, the result
+ * of all writing to this OutputStream.
+ *
+ * @return returns the content of the internal StringBuffer
+ */
+ public String toString() {
+ return buf.toString();
+ }
+
+ /**
+ * Sets the internal StringBuffer to null.
+ */
+ public void close() {
+ buf = null;
+
+ }
+
+ /**
+ * Writes and appends a byte array to StringOutputStream.
+ *
+ * @param b the byte array
+ * @param off the byte array starting index
+ * @param len the number of bytes from byte array to write to the stream
+ */
+ public void write(byte[] b, int off, int len) {
+ if ((off < 0) || (len < 0) || (off + len) > b.length) {
+ throw new IndexOutOfBoundsException(
+ "StringOutputStream.write: Parameters out of bounds.");
+ }
+ byte[] bytes = new byte[len];
+ for (int i = 0; i < len; i++) {
+ bytes[i] = b[off];
+ off++;
+ }
+ buf.append(toCharArray(bytes));
+ }
+
+ /**
+ * Writes and appends a single byte to StringOutputStream.
+ *
+ * @param b the byte as an int to add
+ */
+ public void write(int b) {
+ buf.append((char) b);
+ }
+
+ /**
+ * Writes and appends a String to StringOutputStream.
+ *
+ * @param s the String to add
+ */
+ public void write(String s) {
+ buf.append(s);
+ }
+
+ /**
+ * Converts byte array to char array.
+ */
+ public static char[] toCharArray(byte[] barr) {
+ if (barr == null) {
+ return null;
+ }
+ char[] carr = new char[barr.length];
+ for (int i = 0; i < barr.length; i++) {
+ carr[i] = (char) barr[i];
+ }
+ return carr;
+ }
+}
diff --git a/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/XMLReport.java b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/XMLReport.java
new file mode 100644
index 0000000..6d3a722
--- /dev/null
+++ b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/XMLReport.java
@@ -0,0 +1,312 @@
+/*
+ * 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.plugin;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import org.codehaus.plexus.util.IOUtil;
+import org.codehaus.plexus.util.xml.PrettyPrintXMLWriter;
+import org.codehaus.plexus.util.xml.Xpp3Dom;
+import org.codehaus.plexus.util.xml.Xpp3DomWriter;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * This class generates test result as XML files compatible with Surefire.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class XMLReport extends Report {
+
+ /**
+ * List of results.
+ */
+ private List results = new ArrayList();
+
+ /**
+ * A test ends successfully.
+ * @param test the test executed successfully.
+ */
+ public void testSucceeded(Test test) {
+ super.testSucceeded();
+
+ long runTime = this.endTime - this.startTime;
+
+ Xpp3Dom testCase = createTestElement(test, runTime);
+
+ results.add(testCase);
+ }
+
+ /**
+ * A test throws an unexpected errors.
+ * @param test the test in error
+ * @param e the thrown exception
+ */
+ public void testError(Test test, Throwable e, String out, String err, String log) {
+ super.testError(test);
+
+ writeTestProblems(test, e, "error", out, err, log);
+ }
+
+ /**
+ * A test fails.
+ * @param test the failing test
+ * @param e the thrown failure
+ */
+ public void testFailed(Test test, Throwable e, String out, String err, String log) {
+ super.testFailed(test);
+
+ writeTestProblems(test, e, "failure", out, err, log);
+ }
+
+ /**
+ * Utility method writing failed and in error test result in the report.
+ * @param test the test
+ * @param e the thrown error
+ * @param name type of failure ("error" or "failure")
+ */
+ private void writeTestProblems(Test test, Throwable e, String name, String out, String err, String log) {
+
+ long runTime = endTime - startTime;
+
+ Xpp3Dom testCase = createTestElement(test, runTime);
+
+ Xpp3Dom element = createElement(testCase, name);
+
+ String stackTrace = getStackTrace(test, e);
+
+ Throwable t = e;
+
+ if (t != null) {
+
+ String message = t.getMessage();
+
+ if (message != null) {
+ element.setAttribute("message", message);
+
+ element.setAttribute("type",
+ (stackTrace.indexOf(":") > -1 ? stackTrace.substring(0,
+ stackTrace.indexOf(":")) : stackTrace));
+ } else {
+ element.setAttribute("type", new StringTokenizer(stackTrace)
+ .nextToken());
+ }
+ }
+
+ if (stackTrace != null) {
+ element.setValue(stackTrace);
+ }
+
+ addOutputStreamElement( out, "system-out", testCase );
+
+ addOutputStreamElement( err, "system-err", testCase );
+
+ addOutputStreamElement( log, "log-service", testCase );
+
+ results.add(testCase);
+ }
+
+ /**
+ * Generates the XML reports.
+ * @param test the test
+ * @param tr the test result
+ * @param reportsDirectory the directory in which reports are created.
+ * @param bc the bundle context (to get installed bundles)
+ * @throws Exception when the XML report cannot be generated correctly
+ */
+ public void generateReport(Test test, TestResult tr, File reportsDirectory,
+ BundleContext bc) throws Exception {
+ long runTime = this.endTime - this.startTime;
+
+ Xpp3Dom testSuite = createTestSuiteElement(test, runTime);
+
+ showProperties(testSuite, bc);
+
+ testSuite.setAttribute("tests", String.valueOf(tr.runCount()));
+
+ testSuite.setAttribute("errors", String.valueOf(tr.errorCount()));
+
+ testSuite.setAttribute("failures", String.valueOf(tr.failureCount()));
+
+ for (Iterator i = results.iterator(); i.hasNext();) {
+ Xpp3Dom testcase = (Xpp3Dom) i.next();
+ testSuite.addChild(testcase);
+ }
+
+ File reportFile = new File(reportsDirectory, "TEST-"
+ + getReportName(test).replace(' ', '_') + ".xml");
+
+ File reportDir = reportFile.getParentFile();
+
+ reportDir.mkdirs();
+
+ PrintWriter writer = null;
+
+ try {
+ writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(
+ new FileOutputStream(reportFile), "UTF-8")));
+
+ writer.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" + NL);
+
+ Xpp3DomWriter.write(new PrettyPrintXMLWriter(writer), testSuite);
+ } catch (UnsupportedEncodingException e) {
+ throw new Exception("Unable to use UTF-8 encoding", e);
+ } catch (FileNotFoundException e) {
+ throw new Exception("Unable to create file: " + e.getMessage(), e);
+ }
+
+ finally {
+ IOUtil.close(writer);
+ }
+ }
+
+ /**
+ * Creates a XML test case element.
+ * @param test the test
+ * @param runTime the elapsed time to execute the test.
+ * @return the XML element describing the given test.
+ */
+ private Xpp3Dom createTestElement(Test test, long runTime) {
+ Xpp3Dom testCase = new Xpp3Dom("testcase");
+ testCase.setAttribute("name", getReportName(test));
+ testCase.setAttribute("time", Long.toString(runTime) + " sec");
+ return testCase;
+ }
+
+ /**
+ * Creates a XML test suite element.
+ * @param test the test
+ * @param runTime the elapsed time to execute the test suite.
+ * @return the XML element describing the given test suite.
+ */
+ private Xpp3Dom createTestSuiteElement(Test test, long runTime) {
+ Xpp3Dom testCase = new Xpp3Dom("testsuite");
+ testCase.setAttribute("name", getReportName(test));
+ testCase.setAttribute("time", Long.toString(runTime) + " sec");
+ return testCase;
+ }
+
+ /**
+ * Computes report name.
+ * @param test the test
+ * @return the report name
+ */
+ private static String getReportName(Test test) {
+ String report = test.toString();
+
+ if (report.indexOf("(") > 0) {
+ report = report.substring(0, report.indexOf("("));
+ }
+ return report;
+ }
+
+ /**
+ * Creates an XML element
+ * @param element the parent element
+ * @param name the name of the element to create
+ * @return the resulting XML tree.
+ */
+ private Xpp3Dom createElement(Xpp3Dom element, String name) {
+ Xpp3Dom component = new Xpp3Dom(name);
+
+ element.addChild(component);
+
+ return component;
+ }
+
+ /**
+ * Adds system properties to the XML report.
+ * This method also adds installed bundles.
+ * @param testSuite the XML element.
+ * @param bc the bundle context
+ */
+ private void showProperties(Xpp3Dom testSuite, BundleContext bc) {
+ Xpp3Dom properties = createElement(testSuite, "properties");
+
+ Properties systemProperties = System.getProperties();
+
+ if (systemProperties != null) {
+ Enumeration propertyKeys = systemProperties.propertyNames();
+
+ while (propertyKeys.hasMoreElements()) {
+ String key = (String) propertyKeys.nextElement();
+
+ String value = systemProperties.getProperty(key);
+
+ if (value == null) {
+ value = "null";
+ }
+
+ Xpp3Dom property = createElement(properties, "property");
+
+ property.setAttribute("name", key);
+
+ property.setAttribute("value", value);
+
+ }
+ }
+
+ Xpp3Dom buns = createElement(properties, "bundles");
+ Bundle[] bundles = bc.getBundles();
+ for (int i = 0; i < bundles.length; i++) {
+ String sn = bundles[i].getSymbolicName();
+ String state = "UNKNOWN";
+ switch (bundles[i].getState()) {
+ case Bundle.ACTIVE:
+ state = "ACTIVE";
+ break;
+ case Bundle.INSTALLED:
+ state = "INSTALLED";
+ break;
+ case Bundle.RESOLVED:
+ state = "RESOLVED";
+ break;
+ case Bundle.UNINSTALLED:
+ state = "UNINSTALLED";
+ break;
+ }
+ Xpp3Dom bundle = createElement(buns, "bundle");
+ bundle.setAttribute("symbolic-name", sn);
+ bundle.setAttribute("state", state);
+ }
+
+ }
+
+ private void addOutputStreamElement(String stdOut, String name,
+ Xpp3Dom testCase) {
+ if (stdOut != null && stdOut.trim().length() > 0) {
+ createElement(testCase, name).setValue(stdOut);
+ }
+ }
+
+}
diff --git a/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/log/LogServiceImpl.java b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/log/LogServiceImpl.java
new file mode 100644
index 0000000..aaa6fe0
--- /dev/null
+++ b/ipojo/examples/junit4osgi/maven-junit4osgi-plugin/src/main/java/org/apache/felix/ipojo/junit4osgi/plugin/log/LogServiceImpl.java
@@ -0,0 +1,185 @@
+/*
+ * 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.plugin.log;
+
+import org.apache.felix.ipojo.junit4osgi.plugin.StringOutputStream;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.log.LogService;
+
+/**
+ * An implementation of the log service to collect logged messages.
+ * This service implementation is also {@link BundleActivator} and is
+ * activated when the embedded OSGi platform starts.
+ * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
+ */
+public class LogServiceImpl implements LogService, BundleActivator {
+
+ /**
+ * Default output stream (not collected).
+ */
+ private StringOutputStream defaultStream;
+
+ /**
+ * Collected output stream.
+ */
+ private StringOutputStream outputStream;
+
+ /**
+ * Creates the log service object.
+ */
+ public LogServiceImpl() {
+ defaultStream = new StringOutputStream();
+ }
+
+ /**
+ * Enables the log messages collection.
+ */
+ public void enableOutputStream() {
+ outputStream = new StringOutputStream();
+ }
+
+ /**
+ * Get collected log messages.
+ * @return the String containing the logged messages.
+ */
+ public String getLoggedMessages() {
+ return outputStream.toString();
+ }
+
+ /**
+ * Re-initializes the collected message list.
+ */
+ public void reset() {
+ outputStream = null;
+ }
+
+ /**
+ * Logs a message.
+ * @param arg0 the log level
+ * @param arg1 the message
+ * @see org.osgi.service.log.LogService#log(int, java.lang.String)
+ */
+ public void log(int arg0, String arg1) {
+ write(computeLogMessage(arg0, arg1, null));
+ }
+
+ /**
+ * Logs a message with an attached exception.
+ * @param arg0 the log level
+ * @param arg1 the message
+ * @param arg2 the associated exception
+ * @see org.osgi.service.log.LogService#log(int, java.lang.String, java.lang.Throwable)
+ */
+ public void log(int arg0, String arg1, Throwable arg2) {
+ write(computeLogMessage(arg0, arg1, arg2));
+ }
+
+ /**
+ * Logs a message raised by the given service reference.
+ * @param arg0 the service reference
+ * @param arg1 the log level
+ * @param arg2 the message
+ * @see org.osgi.service.log.LogService#log(org.osgi.framework.ServiceReference, int, java.lang.String)
+ */
+ public void log(ServiceReference arg0, int arg1, String arg2) {
+ write(computeLogMessage(arg1, arg2, null));
+ }
+
+ /**
+ * Logs a message raised by the given service reference
+ * associated with an exception.
+ * @param arg0 the service reference
+ * @param arg1 the log level
+ * @param arg2 the message
+ * @param arg3 the exception
+ * @see org.osgi.service.log.LogService#log(org.osgi.framework.ServiceReference, int, java.lang.String)
+ */
+ public void log(ServiceReference arg0, int arg1, String arg2, Throwable arg3) {
+ write(computeLogMessage(arg1, arg2, arg3));
+ }
+
+ /**
+ * Computes the string from the message.
+ * @param level the log level
+ * @param msg the message
+ * @param exception the exception (can be <code>null</code>
+ * @return the resulting String
+ */
+ private String computeLogMessage(int level, String msg, Throwable exception) {
+ String message = null;
+ switch (level) {
+ case LogService.LOG_DEBUG:
+ message = "[DEBUG] " + msg;
+ break;
+ case LogService.LOG_ERROR:
+ message = "[ERROR] " + msg;
+ break;
+ case LogService.LOG_INFO:
+ message = "[INFO] " + msg;
+ break;
+ case LogService.LOG_WARNING:
+ message = "[WARNING] " + msg;
+ break;
+ }
+
+ if (exception != null) {
+ message = message + " : " + exception.getMessage() + "\n";
+ }
+
+ return message;
+ }
+
+ /**
+ * Writes the given message in the adequate output stream.
+ * @param log the message
+ */
+ public void write(String log) {
+ if (outputStream != null) {
+ outputStream.write(log);
+ } else {
+ defaultStream.write(log);
+ }
+ }
+
+ /**
+ * Stars the log service implementation:
+ * Registers the service.
+ * @param bc the bundle context.
+ * @throws Exception should not happen.
+ * @see org.osgi.framework.BundleActivator#start(org.osgi.framework.BundleContext)
+ */
+ public void start(BundleContext bc) throws Exception {
+ bc.registerService(LogService.class.getName(), this, null);
+ }
+
+ /**
+ * Stops the log service implementation.
+ * Does nothing.
+ * @param arg0 the bundle context
+ * @throws Exception should not happen.
+ * @see org.osgi.framework.BundleActivator#stop(org.osgi.framework.BundleContext)
+ */
+ public void stop(BundleContext arg0) throws Exception {
+ // Nothing to do.
+
+ }
+
+}