diff options
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.java | 1018 |
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(); + } + +} |