summaryrefslogtreecommitdiffstats
path: root/maven-plugins/tags/maven-osgi-junit-plugin-1.0/src/main/java/org/apache/tuscany/maven/plugin/surefire/OSGiSurefireBooter.java
diff options
context:
space:
mode:
Diffstat (limited to 'maven-plugins/tags/maven-osgi-junit-plugin-1.0/src/main/java/org/apache/tuscany/maven/plugin/surefire/OSGiSurefireBooter.java')
-rw-r--r--maven-plugins/tags/maven-osgi-junit-plugin-1.0/src/main/java/org/apache/tuscany/maven/plugin/surefire/OSGiSurefireBooter.java1018
1 files changed, 1018 insertions, 0 deletions
diff --git a/maven-plugins/tags/maven-osgi-junit-plugin-1.0/src/main/java/org/apache/tuscany/maven/plugin/surefire/OSGiSurefireBooter.java b/maven-plugins/tags/maven-osgi-junit-plugin-1.0/src/main/java/org/apache/tuscany/maven/plugin/surefire/OSGiSurefireBooter.java
new file mode 100644
index 0000000000..e59c432f69
--- /dev/null
+++ b/maven-plugins/tags/maven-osgi-junit-plugin-1.0/src/main/java/org/apache/tuscany/maven/plugin/surefire/OSGiSurefireBooter.java
@@ -0,0 +1,1018 @@
+package org.apache.tuscany.maven.plugin.surefire;
+
+/*
+ * 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.
+ */
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.maven.surefire.Surefire;
+import org.apache.maven.surefire.booter.SurefireBooterForkException;
+import org.apache.maven.surefire.booter.SurefireExecutionException;
+import org.apache.maven.surefire.booter.output.FileOutputConsumerProxy;
+import org.apache.maven.surefire.booter.output.ForkingStreamConsumer;
+import org.apache.maven.surefire.booter.output.OutputConsumer;
+import org.apache.maven.surefire.booter.output.StandardOutputConsumer;
+import org.apache.maven.surefire.booter.output.SupressFooterOutputConsumerProxy;
+import org.apache.maven.surefire.booter.output.SupressHeaderOutputConsumerProxy;
+import org.apache.maven.surefire.booter.shade.org.codehaus.plexus.util.IOUtil;
+import org.apache.maven.surefire.booter.shade.org.codehaus.plexus.util.StringUtils;
+import org.apache.maven.surefire.booter.shade.org.codehaus.plexus.util.cli.Commandline;
+import org.apache.maven.surefire.booter.shade.org.codehaus.plexus.util.cli.StreamConsumer;
+import org.apache.maven.surefire.testset.TestSetFailedException;
+import org.apache.maven.surefire.util.NestedRuntimeException;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Derived from surefire-booter 2.4.3
+ */
+public class OSGiSurefireBooter {
+ private static final String TEST_SUITE_PROPERTY_PREFIX = "testSuite.";
+ private static final String REPORT_PROPERTY_PREFIX = "report.";
+ private static final String PARAMS_SUFIX = ".params";
+ private static final String TYPES_SUFIX = ".types";
+
+ /**
+ * A classloader that delegates an OSGi bundle
+ */
+ static class BundleClassLoader extends ClassLoader {
+ private Bundle bundle;
+
+ public BundleClassLoader(Bundle bundle, ClassLoader parent) {
+ super(parent);
+ this.bundle = bundle;
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ return bundle.loadClass(name);
+ }
+
+ @Override
+ protected URL findResource(String name) {
+ return bundle.getResource(name);
+ }
+
+ @Override
+ protected Enumeration<URL> findResources(String name) throws IOException {
+ return bundle.getResources(name);
+ }
+
+ }
+
+ private List reports = new ArrayList();
+
+ /**
+ * Test classpath
+ */
+ private List classPathUrls = new ArrayList();
+
+ /**
+ * surefire provider classpath
+ */
+ private List surefireClassPathUrls = new ArrayList();
+
+ /**
+ * surefire boot classpath
+ */
+ private List surefireBootClassPathUrls = new ArrayList();
+
+ private List testSuites = new ArrayList();
+
+ private boolean failIfNoTests = false;
+
+ private int forkedProcessTimeoutInSeconds = 0;
+
+ private boolean redirectTestOutputToFile = false;
+
+ // ----------------------------------------------------------------------
+ //
+ // ----------------------------------------------------------------------
+
+ private ForkConfiguration forkConfiguration;
+
+ public static final int TESTS_SUCCEEDED_EXIT_CODE = 0;
+
+ public static final int TESTS_FAILED_EXIT_CODE = 255;
+
+ public static final int NO_TESTS_EXIT_CODE = 254;
+
+ private static Method assertionStatusMethod;
+
+ /**
+ * @deprecated because the OSGi classloader is really isolated - no parent.
+ */
+ private boolean childDelegation = true;
+
+ private File reportsDirectory;
+
+ /**
+ * This field is set to true if it's running from main. It's used to help decide what classloader to use.
+ */
+ private final boolean isForked;
+
+ /**
+ * Whether to enable assertions or not (can be affected by the fork arguments, and the ability to do so based on the
+ * JVM).
+ */
+ private boolean enableAssertions;
+
+ static {
+ try {
+ assertionStatusMethod =
+ ClassLoader.class.getMethod("setDefaultAssertionStatus", new Class[] {boolean.class});
+ } catch (NoSuchMethodException e) {
+ assertionStatusMethod = null;
+ }
+ }
+
+ public OSGiSurefireBooter() {
+ isForked = false;
+ }
+
+ private OSGiSurefireBooter(boolean isForked) {
+ this.isForked = isForked;
+ }
+
+ // ----------------------------------------------------------------------
+ // Accessors
+ // ----------------------------------------------------------------------
+
+ public void addReport(String report) {
+ addReport(report, null);
+ }
+
+ public void addReport(String report, Object[] constructorParams) {
+ reports.add(new Object[] {report, constructorParams});
+ }
+
+ public void addTestSuite(String suiteClassName, Object[] constructorParams) {
+ testSuites.add(new Object[] {suiteClassName, constructorParams});
+ }
+
+ public void addClassPathUrl(String path) {
+ if (!classPathUrls.contains(path)) {
+ classPathUrls.add(path);
+ }
+ }
+
+ public void addSurefireClassPathUrl(String path) {
+ if (!surefireClassPathUrls.contains(path)) {
+ surefireClassPathUrls.add(path);
+ }
+ }
+
+ public void addSurefireBootClassPathUrl(String path) {
+ if (!surefireBootClassPathUrls.contains(path)) {
+ surefireBootClassPathUrls.add(path);
+ }
+ }
+
+ /**
+ * Setting this to true will cause a failure if there are no tests to run
+ *
+ * @param redirectTestOutputToFile
+ */
+ public void setFailIfNoTests(boolean failIfNoTests) {
+ this.failIfNoTests = failIfNoTests;
+ }
+
+ /**
+ * When forking, setting this to true will make the test output to be saved in a file instead of showing it on the
+ * standard output
+ *
+ * @param redirectTestOutputToFile
+ */
+ public void setRedirectTestOutputToFile(boolean redirectTestOutputToFile) {
+ this.redirectTestOutputToFile = redirectTestOutputToFile;
+ }
+
+ /**
+ * Set the directory where reports will be saved
+ *
+ * @param reportsDirectory the directory
+ */
+ public void setReportsDirectory(File reportsDirectory) {
+ this.reportsDirectory = reportsDirectory;
+ }
+
+ /**
+ * Get the directory where reports will be saved
+ */
+ public File getReportsDirectory() {
+ return reportsDirectory;
+ }
+
+ public void setForkConfiguration(ForkConfiguration forkConfiguration) {
+ this.forkConfiguration = forkConfiguration;
+ }
+
+ public boolean isForking() {
+ return forkConfiguration.isForking();
+ }
+
+ public int run() throws SurefireBooterForkException, SurefireExecutionException {
+ int result;
+
+ if (ForkConfiguration.FORK_NEVER.equals(forkConfiguration.getForkMode())) {
+ result = runSuitesInProcess();
+ } else if (ForkConfiguration.FORK_ONCE.equals(forkConfiguration.getForkMode())) {
+ result = runSuitesForkOnce();
+ } else if (ForkConfiguration.FORK_ALWAYS.equals(forkConfiguration.getForkMode())) {
+ result = runSuitesForkPerTestSet();
+ } else {
+ throw new SurefireExecutionException("Unknown forkmode: " + forkConfiguration.getForkMode(), null);
+ }
+ return result;
+ }
+
+ private int runSuitesInProcess(String testSet, Properties results) throws SurefireExecutionException {
+ if (testSuites.size() != 1) {
+ throw new IllegalArgumentException("Cannot only specify testSet for single test suites");
+ }
+
+ // TODO: replace with plexus
+
+ // noinspection CatchGenericClass,OverlyBroadCatchBlock
+ ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ start();
+ } catch (Exception e1) {
+ throw new SurefireExecutionException(e1.getMessage(), e1);
+ }
+ try {
+ ClassLoader testsClassLoader =
+ useSystemClassLoader() ? ClassLoader.getSystemClassLoader() : createClassLoader(classPathUrls,
+ null,
+ childDelegation);
+
+ // TODO: assertions = true shouldn't be required for this CL if we had proper separation (see TestNG)
+ ClassLoader surefireClassLoader = createClassLoader(surefireClassPathUrls, testsClassLoader);
+
+ Class surefireClass = surefireClassLoader.loadClass(Surefire.class.getName());
+
+ Object surefire = surefireClass.newInstance();
+
+ Method run =
+ surefireClass.getMethod("run", new Class[] {List.class, Object[].class, String.class,
+ ClassLoader.class, ClassLoader.class, Properties.class,
+ Boolean.class});
+
+ Thread.currentThread().setContextClassLoader(testsClassLoader);
+
+ Integer result =
+ (Integer)run.invoke(surefire, new Object[] {reports, testSuites.get(0), testSet, surefireClassLoader,
+ testsClassLoader, results, new Boolean(failIfNoTests)});
+
+ return result.intValue();
+ } catch (InvocationTargetException e) {
+ throw new SurefireExecutionException(e.getTargetException().getMessage(), e.getTargetException());
+ } catch (Exception e) {
+ throw new SurefireExecutionException("Unable to instantiate and execute Surefire", e);
+ } finally {
+ stop();
+ Thread.currentThread().setContextClassLoader(oldContextClassLoader);
+ }
+ }
+
+ private int runSuitesInProcess() throws SurefireExecutionException {
+ // TODO: replace with plexus
+
+ // noinspection CatchGenericClass,OverlyBroadCatchBlock
+ ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
+ try {
+ start();
+ } catch (Exception e1) {
+ throw new SurefireExecutionException(e1.getMessage(), e1);
+ }
+
+ try {
+ // The test classloader must be constructed first to avoid issues with commons-logging until we properly
+ // separate the TestNG classloader
+ ClassLoader testsClassLoader;
+ String testClassPath = getTestClassPathAsString();
+ System.setProperty("surefire.test.class.path", testClassPath);
+ if (useManifestOnlyJar()) {
+ // testsClassLoader = getClass().getClassLoader(); // ClassLoader.getSystemClassLoader()
+ testsClassLoader = createClassLoader(classPathUrls, null, childDelegation);
+ // SUREFIRE-459, trick the app under test into thinking its classpath was conventional (instead of a single manifest-only jar)
+ System.setProperty("surefire.real.class.path", System.getProperty("java.class.path"));
+ System.setProperty("java.class.path", testClassPath);
+ } else {
+ testsClassLoader = createClassLoader(classPathUrls, null, childDelegation);
+ }
+
+ ClassLoader surefireClassLoader = createClassLoader(surefireClassPathUrls, testsClassLoader);
+
+ Class surefireClass = surefireClassLoader.loadClass(Surefire.class.getName());
+
+ Object surefire = surefireClass.newInstance();
+
+ Method run =
+ surefireClass.getMethod("run", new Class[] {List.class, List.class, ClassLoader.class,
+ ClassLoader.class, Boolean.class});
+
+ Thread.currentThread().setContextClassLoader(testsClassLoader);
+
+ Integer result =
+ (Integer)run.invoke(surefire, new Object[] {reports, testSuites, surefireClassLoader, testsClassLoader,
+ new Boolean(failIfNoTests)});
+
+ return result.intValue();
+ } catch (InvocationTargetException e) {
+ throw new SurefireExecutionException(e.getTargetException().getMessage(), e.getTargetException());
+ } catch (Exception e) {
+ throw new SurefireExecutionException("Unable to instantiate and execute Surefire", e);
+ } finally {
+ stop();
+ Thread.currentThread().setContextClassLoader(oldContextClassLoader);
+ }
+ }
+
+ private String getTestClassPathAsString() {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < classPathUrls.size(); i++) {
+ sb.append(classPathUrls.get(i)).append(File.pathSeparatorChar);
+ }
+ return sb.toString();
+ }
+
+ private int runSuitesForkOnce() throws SurefireBooterForkException {
+ return forkSuites(testSuites, true, true);
+ }
+
+ private int runSuitesForkPerTestSet() throws SurefireBooterForkException {
+ ClassLoader testsClassLoader;
+ ClassLoader surefireClassLoader;
+ try {
+ testsClassLoader = createClassLoader(classPathUrls, null, false);
+ // TODO: assertions = true shouldn't be required if we had proper separation (see TestNG)
+ surefireClassLoader = createClassLoader(surefireClassPathUrls, testsClassLoader, false);
+ } catch (MalformedURLException e) {
+ throw new SurefireBooterForkException("Unable to create classloader to find test suites", e);
+ }
+
+ int globalResult = 0;
+
+ boolean showHeading = true;
+ Properties properties = new Properties();
+ for (Iterator i = testSuites.iterator(); i.hasNext();) {
+ Object[] testSuite = (Object[])i.next();
+
+ Map testSets = getTestSets(testSuite, testsClassLoader, surefireClassLoader);
+
+ for (Iterator j = testSets.keySet().iterator(); j.hasNext();) {
+ Object testSet = j.next();
+ boolean showFooter = !j.hasNext() && !i.hasNext();
+ int result = forkSuite(testSuite, testSet, showHeading, showFooter, properties);
+ if (result > globalResult) {
+ globalResult = result;
+ }
+ showHeading = false;
+ }
+ }
+
+ return globalResult;
+ }
+
+ private Map getTestSets(Object[] testSuite, ClassLoader testsClassLoader, ClassLoader surefireClassLoader)
+ throws SurefireBooterForkException {
+ String className = (String)testSuite[0];
+
+ Object[] params = (Object[])testSuite[1];
+
+ Object suite;
+ try {
+ suite = Surefire.instantiateObject(className, params, surefireClassLoader);
+ } catch (TestSetFailedException e) {
+ throw new SurefireBooterForkException(e.getMessage(), e.getCause());
+ } catch (ClassNotFoundException e) {
+ throw new SurefireBooterForkException("Unable to find class for test suite '" + className + "'", e);
+ } catch (NoSuchMethodException e) {
+ throw new SurefireBooterForkException("Unable to find appropriate constructor for test suite '" + className
+ + "': "
+ + e.getMessage(), e);
+ }
+
+ Map testSets;
+ try {
+ Method m = suite.getClass().getMethod("locateTestSets", new Class[] {ClassLoader.class});
+
+ testSets = (Map)m.invoke(suite, new Object[] {testsClassLoader});
+ } catch (IllegalAccessException e) {
+ throw new SurefireBooterForkException("Error obtaining test sets", e);
+ } catch (NoSuchMethodException e) {
+ throw new SurefireBooterForkException("Error obtaining test sets", e);
+ } catch (InvocationTargetException e) {
+ throw new SurefireBooterForkException(e.getTargetException().getMessage(), e.getTargetException());
+ }
+ return testSets;
+ }
+
+ private int forkSuites(List testSuites, boolean showHeading, boolean showFooter) throws SurefireBooterForkException {
+ Properties properties = new Properties();
+
+ setForkProperties(testSuites, properties);
+
+ return fork(properties, showHeading, showFooter);
+ }
+
+ private int forkSuite(Object[] testSuite,
+ Object testSet,
+ boolean showHeading,
+ boolean showFooter,
+ Properties properties) throws SurefireBooterForkException {
+ setForkProperties(Collections.singletonList(testSuite), properties);
+
+ if (testSet instanceof String) {
+ properties.setProperty("testSet", (String)testSet);
+ }
+
+ return fork(properties, showHeading, showFooter);
+ }
+
+ private void setForkProperties(List testSuites, Properties properties) {
+ addPropertiesForTypeHolder(reports, properties, REPORT_PROPERTY_PREFIX);
+ addPropertiesForTypeHolder(testSuites, properties, TEST_SUITE_PROPERTY_PREFIX);
+
+ for (int i = 0; i < classPathUrls.size(); i++) {
+ String url = (String)classPathUrls.get(i);
+ properties.setProperty("classPathUrl." + i, url);
+ }
+
+ for (int i = 0; i < surefireClassPathUrls.size(); i++) {
+ String url = (String)surefireClassPathUrls.get(i);
+ properties.setProperty("surefireClassPathUrl." + i, url);
+ }
+
+ properties.setProperty("childDelegation", String.valueOf(childDelegation));
+ properties.setProperty("enableAssertions", String.valueOf(enableAssertions));
+ properties.setProperty("useSystemClassLoader", String.valueOf(useSystemClassLoader()));
+ properties.setProperty("useManifestOnlyJar", String.valueOf(useManifestOnlyJar()));
+ properties.setProperty("failIfNoTests", String.valueOf(failIfNoTests));
+ properties.setProperty("mainBundleName", mainBundleName);
+ }
+
+ private File writePropertiesFile(String name, Properties properties) throws IOException {
+ File file = File.createTempFile(name, "tmp");
+ if (!forkConfiguration.isDebug()) {
+ file.deleteOnExit();
+ }
+
+ writePropertiesFile(file, name, properties);
+
+ return file;
+ }
+
+ private void writePropertiesFile(File file, String name, Properties properties) throws IOException {
+ FileOutputStream out = new FileOutputStream(file);
+
+ try {
+ properties.store(out, name);
+ } finally {
+ IOUtil.close(out);
+ }
+ }
+
+ private void addPropertiesForTypeHolder(List typeHolderList, Properties properties, String propertyPrefix) {
+ for (int i = 0; i < typeHolderList.size(); i++) {
+ Object[] report = (Object[])typeHolderList.get(i);
+
+ String className = (String)report[0];
+ Object[] params = (Object[])report[1];
+
+ properties.setProperty(propertyPrefix + i, className);
+
+ if (params != null) {
+ String paramProperty = convert(params[0]);
+ String typeProperty = params[0].getClass().getName();
+ for (int j = 1; j < params.length; j++) {
+ paramProperty += "|";
+ typeProperty += "|";
+ if (params[j] != null) {
+ paramProperty += convert(params[j]);
+ typeProperty += params[j].getClass().getName();
+ }
+ }
+ properties.setProperty(propertyPrefix + i + PARAMS_SUFIX, paramProperty);
+ properties.setProperty(propertyPrefix + i + TYPES_SUFIX, typeProperty);
+ }
+ }
+ }
+
+ private static String convert(Object param) {
+ if (param instanceof File[]) {
+ File[] files = (File[])param;
+ return "[" + StringUtils.join(files, ",") + "]";
+ } else if (param instanceof Properties) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ try {
+ ((Properties)param).store(baos, "");
+ return new String(baos.toByteArray(), "8859_1");
+ } catch (Exception e) {
+ throw new RuntimeException("bug in property conversion", e);
+ }
+ } else {
+ return param.toString();
+ }
+ }
+
+ private final boolean useSystemClassLoader() {
+ return forkConfiguration.isUseSystemClassLoader() && (isForked || forkConfiguration.isForking());
+ }
+
+ private final boolean useManifestOnlyJar() {
+ return forkConfiguration.isUseSystemClassLoader() && forkConfiguration.isUseManifestOnlyJar();
+ }
+
+ private int fork(Properties properties, boolean showHeading, boolean showFooter) throws SurefireBooterForkException {
+ File surefireProperties;
+ File systemProperties = null;
+ try {
+ surefireProperties = writePropertiesFile("surefire", properties);
+ if (forkConfiguration.getSystemProperties() != null) {
+ systemProperties = writePropertiesFile("surefire", forkConfiguration.getSystemProperties());
+ }
+ } catch (IOException e) {
+ throw new SurefireBooterForkException("Error creating properties files for forking", e);
+ }
+
+ /*
+ System.out.println("cp: " + classPathUrls);
+ System.out.println("scp: " + surefireClassPathUrls);
+ System.out.println("sbcp: " + surefireBootClassPathUrls);
+ */
+
+ List bootClasspath = new ArrayList(surefireBootClassPathUrls.size());
+
+ bootClasspath.addAll(surefireBootClassPathUrls);
+
+ /**
+ * For OSGi, we don't want to polute the system classpath
+ */
+ /*
+ if (useSystemClassLoader()) {
+ bootClasspath.addAll(classPathUrls);
+ }
+ */
+
+ Commandline cli =
+ forkConfiguration.createCommandLine(bootClasspath, useManifestOnlyJar());
+
+ cli.createArg().setFile(surefireProperties);
+
+ if (systemProperties != null) {
+ cli.createArg().setFile(systemProperties);
+ }
+
+ ForkingStreamConsumer out = getForkingStreamConsumer(showHeading, showFooter, redirectTestOutputToFile);
+
+ StreamConsumer err;
+
+ if (redirectTestOutputToFile) {
+ err = out;
+ } else {
+ err = getForkingStreamConsumer(showHeading, showFooter, redirectTestOutputToFile);
+ }
+
+ if (forkConfiguration.isDebug()) {
+ System.out.println("Forking command line: " + cli);
+ }
+
+ int returnCode;
+
+ try {
+ returnCode =
+ org.apache.maven.surefire.booter.shade.org.codehaus.plexus.util.cli.CommandLineUtils
+ .executeCommandLine(cli, out, err, forkedProcessTimeoutInSeconds);
+ } catch (org.apache.maven.surefire.booter.shade.org.codehaus.plexus.util.cli.CommandLineException e) {
+ throw new SurefireBooterForkException("Error while executing forked tests.", e);
+ }
+
+ if (redirectTestOutputToFile) {
+ // ensure the FileOutputConsumerProxy flushes/closes the output file
+ try {
+ out.getOutputConsumer().testSetCompleted();
+ } catch (Exception e) {
+ // the FileOutputConsumerProxy might throw an IllegalStateException but that's not of interest now
+ }
+ }
+
+ if (surefireProperties != null && surefireProperties.exists()) {
+ FileInputStream inStream = null;
+ try {
+ inStream = new FileInputStream(surefireProperties);
+
+ properties.load(inStream);
+ } catch (FileNotFoundException e) {
+ throw new SurefireBooterForkException("Unable to reload properties file from forked process", e);
+ } catch (IOException e) {
+ throw new SurefireBooterForkException("Unable to reload properties file from forked process", e);
+ } finally {
+ IOUtil.close(inStream);
+ }
+ }
+
+ return returnCode;
+ }
+
+ private ClassLoader createClassLoader(List classPathUrls, ClassLoader parent) throws MalformedURLException {
+ return createClassLoader(classPathUrls, parent, false);
+ }
+
+ private ClassLoader createClassLoader(List classPathUrls, ClassLoader parent, boolean childDelegation)
+ throws MalformedURLException {
+ Set<URL> urls = new HashSet<URL>();
+
+ for (Iterator i = classPathUrls.iterator(); i.hasNext();) {
+ String url = (String)i.next();
+
+ if (url != null) {
+ File f = new File(url);
+ urls.add(f.toURI().toURL());
+ }
+ }
+ return bundleClassLoader;
+ }
+
+ private static List processStringList(String stringList) {
+ String sl = stringList;
+
+ if (sl.startsWith("[") && sl.endsWith("]")) {
+ sl = sl.substring(1, sl.length() - 1);
+ }
+
+ List list = new ArrayList();
+
+ String[] stringArray = StringUtils.split(sl, ",");
+
+ for (int i = 0; i < stringArray.length; i++) {
+ list.add(stringArray[i].trim());
+ }
+ return list;
+ }
+
+ private static Properties loadProperties(File file) throws IOException {
+ Properties p = new Properties();
+
+ if (file != null && file.exists()) {
+ FileInputStream inStream = new FileInputStream(file);
+ try {
+ p.load(inStream);
+ } finally {
+ IOUtil.close(inStream);
+ }
+ }
+
+ return p;
+ }
+
+ private static void setSystemProperties(File file) throws IOException {
+ Properties p = loadProperties(file);
+
+ for (Iterator i = p.keySet().iterator(); i.hasNext();) {
+ String key = (String)i.next();
+
+ System.setProperty(key, p.getProperty(key));
+ }
+ }
+
+ private static Object[] constructParamObjects(String paramProperty, String typeProperty) {
+ Object[] paramObjects = null;
+ if (paramProperty != null) {
+ // bit of a glitch that it need sto be done twice to do an odd number of vertical bars (eg |||, |||||).
+ String[] params =
+ StringUtils.split(StringUtils.replace(StringUtils.replace(paramProperty, "||", "| |"), "||", "| |"),
+ "|");
+ String[] types =
+ StringUtils
+ .split(StringUtils.replace(StringUtils.replace(typeProperty, "||", "| |"), "||", "| |"), "|");
+
+ paramObjects = new Object[params.length];
+
+ for (int i = 0; i < types.length; i++) {
+ if (types[i].trim().length() == 0) {
+ params[i] = null;
+ } else if (types[i].equals(String.class.getName())) {
+ paramObjects[i] = params[i];
+ } else if (types[i].equals(File.class.getName())) {
+ paramObjects[i] = new File(params[i]);
+ } else if (types[i].equals(File[].class.getName())) {
+ List stringList = processStringList(params[i]);
+ File[] fileList = new File[stringList.size()];
+ for (int j = 0; j < stringList.size(); j++) {
+ fileList[j] = new File((String)stringList.get(j));
+ }
+ paramObjects[i] = fileList;
+ } else if (types[i].equals(ArrayList.class.getName())) {
+ paramObjects[i] = processStringList(params[i]);
+ } else if (types[i].equals(Boolean.class.getName())) {
+ paramObjects[i] = Boolean.valueOf(params[i]);
+ } else if (types[i].equals(Integer.class.getName())) {
+ paramObjects[i] = Integer.valueOf(params[i]);
+ } else if (types[i].equals(Properties.class.getName())) {
+ final Properties result = new Properties();
+ final String value = params[i];
+ try {
+ ByteArrayInputStream bais = new ByteArrayInputStream(value.getBytes("8859_1"));
+ result.load(bais);
+ } catch (Exception e) {
+ throw new RuntimeException("bug in property conversion", e);
+ }
+ paramObjects[i] = result;
+ } else {
+ // TODO: could attempt to construct with a String constructor if needed
+ throw new IllegalArgumentException("Unknown parameter type: " + types[i]);
+ }
+ }
+ }
+ return paramObjects;
+ }
+
+ /**
+ * This method is invoked when Surefire is forked - this method parses and organizes the arguments passed to it and
+ * then calls the Surefire class' run method. <p/> The system exit code will be 1 if an exception is thrown.
+ *
+ * @param args
+ */
+ public static void main(String[] args) throws Throwable {
+ // noinspection CatchGenericClass,OverlyBroadCatchBlock
+ try {
+ if (args.length > 1) {
+ setSystemProperties(new File(args[1]));
+ }
+
+ File surefirePropertiesFile = new File(args[0]);
+ Properties p = loadProperties(surefirePropertiesFile);
+
+ SortedMap classPathUrls = new TreeMap();
+
+ SortedMap surefireClassPathUrls = new TreeMap();
+
+ OSGiSurefireBooter surefireBooter = new OSGiSurefireBooter(true);
+
+ ForkConfiguration forkConfiguration = new ForkConfiguration();
+ forkConfiguration.setForkMode("never");
+ surefireBooter.setForkConfiguration(forkConfiguration);
+
+ for (Enumeration e = p.propertyNames(); e.hasMoreElements();) {
+ String name = (String)e.nextElement();
+
+ if (name.startsWith(REPORT_PROPERTY_PREFIX) && !name.endsWith(PARAMS_SUFIX)
+ && !name.endsWith(TYPES_SUFIX)) {
+ String className = p.getProperty(name);
+
+ String params = p.getProperty(name + PARAMS_SUFIX);
+ String types = p.getProperty(name + TYPES_SUFIX);
+ surefireBooter.addReport(className, constructParamObjects(params, types));
+ } else if (name.startsWith(TEST_SUITE_PROPERTY_PREFIX) && !name.endsWith(PARAMS_SUFIX)
+ && !name.endsWith(TYPES_SUFIX)) {
+ String className = p.getProperty(name);
+
+ String params = p.getProperty(name + PARAMS_SUFIX);
+ String types = p.getProperty(name + TYPES_SUFIX);
+ surefireBooter.addTestSuite(className, constructParamObjects(params, types));
+ } else if (name.startsWith("classPathUrl.")) {
+ classPathUrls.put(Integer.valueOf(name.substring(name.indexOf('.') + 1)), p.getProperty(name));
+ } else if (name.startsWith("surefireClassPathUrl.")) {
+ surefireClassPathUrls.put(Integer.valueOf(name.substring(name.indexOf('.') + 1)), p
+ .getProperty(name));
+ } else if (name.startsWith("surefireBootClassPathUrl.")) {
+ surefireBooter.addSurefireBootClassPathUrl(p.getProperty(name));
+ } else if ("childDelegation".equals(name)) {
+ surefireBooter.childDelegation = Boolean.valueOf(p.getProperty("childDelegation")).booleanValue();
+ } else if ("enableAssertions".equals(name)) {
+ surefireBooter.enableAssertions = Boolean.valueOf(p.getProperty("enableAssertions")).booleanValue();
+ } else if ("useSystemClassLoader".equals(name)) {
+ surefireBooter.forkConfiguration.setUseSystemClassLoader(Boolean.valueOf(p
+ .getProperty("useSystemClassLoader")).booleanValue());
+ } else if ("useManifestOnlyJar".equals(name)) {
+ surefireBooter.forkConfiguration.setUseManifestOnlyJar(Boolean.valueOf(p
+ .getProperty("useManifestOnlyJar")).booleanValue());
+ } else if ("failIfNoTests".equals(name)) {
+ surefireBooter.setFailIfNoTests(Boolean.valueOf(p.getProperty("failIfNoTests")).booleanValue());
+ } else if ("mainBundleName".equals(name)) {
+ surefireBooter.setMainBundleName(p.getProperty("mainBundleName"));
+ }
+ }
+
+ for (Iterator cpi = classPathUrls.keySet().iterator(); cpi.hasNext();) {
+ String url = (String)classPathUrls.get(cpi.next());
+ surefireBooter.addClassPathUrl(url);
+ }
+
+ for (Iterator scpi = surefireClassPathUrls.keySet().iterator(); scpi.hasNext();) {
+ String url = (String)surefireClassPathUrls.get(scpi.next());
+ surefireBooter.addSurefireClassPathUrl(url);
+ }
+
+ String testSet = p.getProperty("testSet");
+ int result;
+ if (testSet != null) {
+ result = surefireBooter.runSuitesInProcess(testSet, p);
+ } else {
+ result = surefireBooter.runSuitesInProcess();
+ }
+
+ surefireBooter.writePropertiesFile(surefirePropertiesFile, "surefire", p);
+
+ // noinspection CallToSystemExit
+ System.exit(result);
+ } catch (Throwable t) {
+ // Just throwing does getMessage() and a local trace - we want to call printStackTrace for a full trace
+ // noinspection UseOfSystemOutOrSystemErr
+ t.printStackTrace(System.err);
+ // noinspection ProhibitedExceptionThrown,CallToSystemExit
+ System.exit(1);
+ }
+ }
+
+ public void setChildDelegation(boolean childDelegation) {
+ this.childDelegation = childDelegation;
+ }
+
+ private ForkingStreamConsumer getForkingStreamConsumer(boolean showHeading,
+ boolean showFooter,
+ boolean redirectTestOutputToFile) {
+ OutputConsumer outputConsumer = new StandardOutputConsumer();
+
+ if (redirectTestOutputToFile) {
+ outputConsumer = new FileOutputConsumerProxy(outputConsumer, getReportsDirectory());
+ }
+
+ if (!showHeading) {
+ outputConsumer = new SupressHeaderOutputConsumerProxy(outputConsumer);
+ }
+ if (!showFooter) {
+ outputConsumer = new SupressFooterOutputConsumerProxy(outputConsumer);
+ }
+
+ return new ForkingStreamConsumer(outputConsumer);
+ }
+
+ public void setEnableAssertions(boolean enableAssertions) {
+ this.enableAssertions = enableAssertions;
+ }
+
+ public void setForkedProcessTimeoutInSeconds(int forkedProcessTimeoutInSeconds) {
+ this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds;
+ }
+
+ private String mainBundleName;
+
+ public void setMainBundleName(String mainBundleName) {
+ this.mainBundleName = mainBundleName;
+ }
+
+ private Object host;
+ private BundleClassLoader bundleClassLoader;
+
+ public Object start() throws Exception {
+ Set<URL> urls = new HashSet<URL>();
+
+ // Merge the two classpaths so that all of them will be OSGi-enabled
+ Set<String> cps = new HashSet<String>(classPathUrls);
+ cps.addAll(surefireClassPathUrls);
+
+ for (String url: cps) {
+ if (url != null) {
+ File f = new File(url);
+ try {
+ urls.add(f.toURI().toURL());
+ } catch (MalformedURLException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ host = createEquinox(urls);
+ BundleContext context = startEquinox(host);
+ Bundle mainBundle = null;
+ for (Bundle bundle : context.getBundles()) {
+ // Fragement bundle cannot be used to load class, use the main bundle
+ if (mainBundleName.equals(bundle.getSymbolicName())) {
+ mainBundle = bundle;
+ break;
+ }
+ }
+
+ bundleClassLoader = new BundleClassLoader(mainBundle, null);
+ if (assertionStatusMethod != null) {
+ try {
+ Object[] args = new Object[] {enableAssertions ? Boolean.TRUE : Boolean.FALSE};
+ assertionStatusMethod.invoke(bundleClassLoader, args);
+ } catch (IllegalAccessException e) {
+ throw new NestedRuntimeException("Unable to access the assertion enablement method", e);
+ } catch (InvocationTargetException e) {
+ throw new NestedRuntimeException("Unable to invoke the assertion enablement method", e);
+ }
+ }
+
+ return host;
+ }
+
+ public void stop() {
+ if (host != null) {
+ try {
+ stopEquinox(host);
+ } catch (Exception e) {
+ throw new IllegalStateException(e);
+ }
+ host = null;
+ bundleClassLoader = null;
+ }
+ }
+
+ private static Object createEquinox(Set<URL> urls) throws Exception {
+ String name = "org.apache.tuscany.sca.node.equinox.launcher.EquinoxHost";
+ Class<?> cls = Class.forName(name);
+ Constructor<?> ctor = cls.getConstructor(Set.class);
+ return ctor.newInstance(urls);
+ }
+
+ private static BundleContext startEquinox(Object host) throws Exception {
+ Method start = host.getClass().getMethod("start");
+ return (BundleContext)start.invoke(host);
+ }
+
+ private static void stopEquinox(Object host) throws Exception {
+ Method stop = host.getClass().getMethod("stop");
+ stop.invoke(host);
+ }
+
+ /**
+ * Returns a string representation of the given bundle.
+ *
+ * @param b
+ * @param verbose
+ * @return
+ */
+ static String string(Bundle bundle, boolean verbose) {
+ StringBuffer sb = new StringBuffer();
+ sb.append(bundle.getBundleId()).append(" ").append(bundle.getSymbolicName());
+ int s = bundle.getState();
+ if ((s & Bundle.UNINSTALLED) != 0) {
+ sb.append(" UNINSTALLED");
+ }
+ if ((s & Bundle.INSTALLED) != 0) {
+ sb.append(" INSTALLED");
+ }
+ if ((s & Bundle.RESOLVED) != 0) {
+ sb.append(" RESOLVED");
+ }
+ if ((s & Bundle.STARTING) != 0) {
+ sb.append(" STARTING");
+ }
+ if ((s & Bundle.STOPPING) != 0) {
+ sb.append(" STOPPING");
+ }
+ if ((s & Bundle.ACTIVE) != 0) {
+ sb.append(" ACTIVE");
+ }
+
+ if (verbose) {
+ sb.append(" ").append(bundle.getLocation());
+ sb.append(" ").append(bundle.getHeaders());
+ }
+ return sb.toString();
+ }
+
+}