diff --git a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
index 5f12a04..4533323 100644
--- a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
+++ b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
@@ -41,6 +41,7 @@ import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
 import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
 import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
+import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
 import org.apache.maven.artifact.versioning.VersionRange;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.plugin.AbstractMojo;
@@ -424,10 +425,28 @@ public class SurefirePlugin
     private int threadCount;
 
     /**
+     * (junitcore only) Indicates that threadCount is per cpu core. Defaults to true
+     *
+     * @parameter expression="${perCoreThreadCount}"
+     * @since 2.5
+     */
+    private String perCoreThreadCount;
+
+    /**
+     * (junitcore only) Indicates that the thread pool will be unlimited. paralell setting and the actual number of classes/methods
+     * will decide. Setting this to true effectively disables perCoreThreadCount and  threadCount
+     *
+     * @parameter expression="${useUnlimitedThreads}"
+     * @since 2.5
+     */
+    private String useUnlimitedThreads;
+    /**
      * (TestNG only) When you use the parallel attribute, TestNG will try to run all your test methods in separate threads, except for
      * methods that depend on each other, which will be run in the same thread in order to respect their order of
      * execution.
      *
+     * JUNIT4.6 Values are classes/methods/both to run in separate threads, as controlled by threadCount.
+     *
      * @parameter expression="${parallel}"
      * @todo test how this works with forking, and console/file output parallelism
      * @since 2.2
@@ -662,6 +681,58 @@ public class SurefirePlugin
         }
     }
 
+    /**
+     * Converts old TestNG configuration parameters over to new properties based configuration
+     * method. (if any are defined the old way)
+     */
+    private void convertJunitCoreParameters()
+    {
+        if ( properties == null )
+        {
+            properties = new Properties();
+        }
+
+        if ( this.parallel != null )
+        {
+            properties.setProperty( "parallel", this.parallel );
+        }
+        if ( this.threadCount > 0 )
+        {
+            properties.setProperty( "threadCount", new Integer( this.threadCount ).toString() );
+        }
+        if ( this.perCoreThreadCount != null )
+        {
+            properties.setProperty( "perCoreThreadCount", perCoreThreadCount);
+        }
+        if ( this.useUnlimitedThreads != null )
+        {
+            properties.setProperty( "useUnlimitedThreads", useUnlimitedThreads);
+        }
+        Artifact configurableParallelComputer = (Artifact) projectArtifactMap.get("org.jdogma.junit:configurable-parallel-computer");
+        properties.setProperty("configurableParallelComputerPresent", Boolean.toString(configurableParallelComputer != null));
+
+    }
+
+    private boolean isJunit47Compatible(Artifact artifact) throws MojoExecutionException {
+        return isWithinVersionSpec(artifact, "[4.7,)");
+    }
+    
+    private boolean isJunit40to46(Artifact artifact)  throws MojoExecutionException {
+        return isWithinVersionSpec(artifact, "[4.0,4.7)");
+    }
+
+    private boolean isWithinVersionSpec(Artifact artifact, String versionSpec) throws MojoExecutionException {
+        try {
+            VersionRange  range = VersionRange.createFromVersionSpec( versionSpec);
+            return range.containsVersion( artifact.getSelectedVersion());
+        } catch (InvalidVersionSpecificationException e) {
+            throw new MojoExecutionException("Bug in junit 4.7 plugin. Please report with stacktrace");
+        } catch (OverConstrainedVersionException e) {
+            throw new MojoExecutionException("Bug in junit 4.7 plugin. Please report with stacktrace");
+        }
+    }
+
+
     private SurefireBooter constructSurefireBooter()
         throws MojoExecutionException, MojoFailureException
     {
@@ -714,7 +785,12 @@ public class SurefirePlugin
                 // different one since its based on the source level, not the JVM. Prune using the filter.
                 addProvider( surefireBooter, "surefire-testng", surefireArtifact.getBaseVersion(), testNgArtifact );
             }
-            else if ( junitArtifact != null && junitArtifact.getBaseVersion().startsWith( "4" ) )
+            else if ( isJunit47Compatible( junitArtifact))
+            {
+                convertJunitCoreParameters();                
+                addProvider( surefireBooter, "surefire-junit47", surefireArtifact.getBaseVersion(), null );
+            }
+            else if ( isJunit40to46( junitArtifact ))
             {
                 addProvider( surefireBooter, "surefire-junit4", surefireArtifact.getBaseVersion(), null );
             }
@@ -806,29 +882,29 @@ public class SurefirePlugin
                 }
             }
 
-            if ( testNgArtifact != null )
-            {
-                surefireBooter.addTestSuite( "org.apache.maven.surefire.testng.TestNGDirectoryTestSuite", new Object[] {
-                    testClassesDirectory, includes, excludes, testSourceDirectory.getAbsolutePath(),
-                    testNgArtifact.getVersion(), testNgArtifact.getClassifier(), properties, reportsDirectory} );
-            }
-            else
-            {
+            if (testNgArtifact != null) {
+                surefireBooter.addTestSuite("org.apache.maven.surefire.testng.TestNGDirectoryTestSuite", new Object[]{
+                        testClassesDirectory, includes, excludes, testSourceDirectory.getAbsolutePath(),
+                        testNgArtifact.getVersion(), testNgArtifact.getClassifier(), properties, reportsDirectory});
+            } else {
                 String junitDirectoryTestSuite;
-                if ( junitArtifact != null && junitArtifact.getBaseVersion() != null &&
-                    junitArtifact.getBaseVersion().startsWith( "4" ) )
-                {
-                    junitDirectoryTestSuite = "org.apache.maven.surefire.junit4.JUnit4DirectoryTestSuite";
-                }
-                else
+                if (isJunit47Compatible(junitArtifact))
                 {
-                    junitDirectoryTestSuite = "org.apache.maven.surefire.junit.JUnitDirectoryTestSuite";
+                    junitDirectoryTestSuite = "org.apache.maven.surefire.junitcore.JUnitCoreDirectoryTestSuite";
+                    getLog().warn( "Props are" + properties.toString());
+                    surefireBooter.addTestSuite(junitDirectoryTestSuite, new Object[]{testClassesDirectory, includes, excludes, properties});
+                } else {
+                    if (isJunit40to46(junitArtifact))
+                    {
+                        junitDirectoryTestSuite = "org.apache.maven.surefire.junit4.JUnit4DirectoryTestSuite";
+                    } else {
+                        // fall back to JUnit, which also contains POJO support. Also it can run
+                        // classes compiled against JUnit since it has a dependency on JUnit itself.
+                        junitDirectoryTestSuite = "org.apache.maven.surefire.junit.JUnitDirectoryTestSuite";
+                    }
+                    surefireBooter.addTestSuite(junitDirectoryTestSuite, new Object[]{testClassesDirectory, includes, excludes});
                 }
 
-                // fall back to JUnit, which also contains POJO support. Also it can run
-                // classes compiled against JUnit since it has a dependency on JUnit itself.
-                surefireBooter.addTestSuite( junitDirectoryTestSuite, new Object[] { testClassesDirectory, includes,
-                    excludes } );
             }
         }
 
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/suite/AbstractDirectoryTestSuite.java b/surefire-api/src/main/java/org/apache/maven/surefire/suite/AbstractDirectoryTestSuite.java
index fb722b4..11b52e2 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/suite/AbstractDirectoryTestSuite.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/suite/AbstractDirectoryTestSuite.java
@@ -20,17 +20,14 @@ package org.apache.maven.surefire.suite;
  */
 
 import org.apache.maven.surefire.Surefire;
+import org.apache.maven.surefire.util.SurefireDirectoryScanner;
 import org.apache.maven.surefire.report.ReportEntry;
 import org.apache.maven.surefire.report.ReporterException;
 import org.apache.maven.surefire.report.ReporterManager;
 import org.apache.maven.surefire.testset.SurefireTestSet;
 import org.apache.maven.surefire.testset.TestSetFailedException;
-import org.codehaus.plexus.util.DirectoryScanner;
-import org.codehaus.plexus.util.StringUtils;
 
 import java.io.File;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -43,27 +40,16 @@ public abstract class AbstractDirectoryTestSuite
 {
     protected static ResourceBundle bundle = ResourceBundle.getBundle( Surefire.SUREFIRE_BUNDLE_NAME );
 
-    private static final String FS = System.getProperty( "file.separator" );
-
-    private File basedir;
-
-    private List includes;
-
-    private List excludes;
-
     protected Map testSets;
 
     private int totalTests;
+    
+    private final SurefireDirectoryScanner surefireDirectoryScanner;
 
-    private static final String[] EMPTY_STRING_ARRAY = new String[0];
 
     protected AbstractDirectoryTestSuite( File basedir, List includes, List excludes )
     {
-        this.basedir = basedir;
-
-        this.includes = new ArrayList( includes );
-
-        this.excludes = new ArrayList( excludes );
+        this.surefireDirectoryScanner = new SurefireDirectoryScanner(basedir, includes, excludes);
     }
 
     public Map locateTestSets( ClassLoader classLoader )
@@ -75,26 +61,13 @@ public abstract class AbstractDirectoryTestSuite
         }
         testSets = new HashMap();
 
-        String[] tests = collectTests( basedir, includes, excludes );
+        Class[] locatedClasses = surefireDirectoryScanner.locateTestClasses( classLoader);
 
-        for ( int i = 0; i < tests.length; i++ )
+        for ( int i = 0; i < locatedClasses.length; i++ )
         {
-            String className = tests[i];
-
-            Class testClass;
-            try
-            {
-                testClass = classLoader.loadClass( className );
-            }
-            catch ( ClassNotFoundException e )
-            {
-                throw new TestSetFailedException( "Unable to create test class '" + className + "'", e );
-            }
-
-            if ( !Modifier.isAbstract( testClass.getModifiers() ) )
-            {
-                SurefireTestSet testSet = createTestSet( testClass, classLoader );
-                
+            Class testClass = locatedClasses[i];
+            SurefireTestSet testSet = createTestSet( testClass, classLoader );
+
                 if ( testSet == null )
                 {
                     continue;
@@ -107,7 +80,6 @@ public abstract class AbstractDirectoryTestSuite
                 testSets.put( testSet.getName(), testSet );
 
                 totalTests++;
-            }
         }
 
         return Collections.unmodifiableMap( testSets );
@@ -177,47 +149,4 @@ public abstract class AbstractDirectoryTestSuite
         return totalTests;
     }
 
-    private String[] collectTests( File basedir, List includes, List excludes )
-    {
-        String[] tests = EMPTY_STRING_ARRAY;
-        if ( basedir.exists() )
-        {
-            DirectoryScanner scanner = new DirectoryScanner();
-
-            scanner.setBasedir( basedir );
-
-            if ( includes != null )
-            {
-                scanner.setIncludes( processIncludesExcludes( includes ) );
-            }
-
-            if ( excludes != null )
-            {
-                scanner.setExcludes( processIncludesExcludes( excludes ) );
-            }
-
-            scanner.scan();
-
-            tests = scanner.getIncludedFiles();
-            for ( int i = 0; i < tests.length; i++ )
-            {
-                String test = tests[i];
-                test = test.substring( 0, test.indexOf( "." ) );
-                tests[i] = test.replace( FS.charAt( 0 ), '.' );
-            }
-        }
-        return tests;
-    }
-
-    private static String[] processIncludesExcludes( List list )
-    {
-        String[] incs = new String[list.size()];
-
-        for ( int i = 0; i < incs.length; i++ )
-        {
-            incs[i] = StringUtils.replace( (String) list.get( i ), "java", "class" );
-
-        }
-        return incs;
-    }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/SurefireDirectoryScanner.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/SurefireDirectoryScanner.java
new file mode 100644
index 0000000..5045d04
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/SurefireDirectoryScanner.java
@@ -0,0 +1,173 @@
+package org.apache.maven.surefire.util;
+/*
+ * 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 org.codehaus.plexus.util.StringUtils;
+import org.apache.maven.surefire.testset.TestSetFailedException;
+import org.apache.maven.surefire.testset.SurefireTestSet;
+
+import java.util.*;
+import java.io.File;
+import java.lang.reflect.Modifier;
+
+/**
+ * Scans directories looking for tests.
+ * @author Karl M. Davis
+ * @author Kristian Rosenvold
+ */
+public class SurefireDirectoryScanner {
+
+    private static final String FS = System.getProperty( "file.separator" );
+    private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+    private final File basedir;
+
+    private final List includes;
+
+    private final List excludes;
+
+    protected Map testSets;
+
+    private int totalTests;
+
+
+    public Map getTestSets() {
+        return testSets;
+    }
+
+    public SurefireDirectoryScanner(File basedir, List includes, List excludes) {
+        this.basedir = basedir;
+        this.includes = includes;
+        this.excludes = excludes;
+    }
+
+    public interface TestSetCreator{
+        SurefireTestSet createTestSet(Class clazz);
+    }
+
+    public Map locateTestSets( ClassLoader classLoader, TestSetCreator testSetCreator )
+        throws TestSetFailedException
+    {
+        if ( testSets != null )
+        {
+            throw new IllegalStateException( "You can't call locateTestSets twice" );
+        }
+        testSets = new HashMap();
+
+        Class[] locatedClasses = locateTestClasses( classLoader);
+
+        for ( int i = 0; i < locatedClasses.length; i++ )
+        {
+            Class testClass = locatedClasses[i];
+            SurefireTestSet testSet = testSetCreator.createTestSet( testClass);
+
+                if ( testSet == null )
+                {
+                    continue;
+                }
+
+                if ( testSets.containsKey( testSet.getName() ) )
+                {
+                    throw new TestSetFailedException( "Duplicate test set '" + testSet.getName() + "'" );
+                }
+                testSets.put( testSet.getName(), testSet );
+
+                totalTests++;
+        }
+
+        return Collections.unmodifiableMap( testSets );
+    }
+
+    public Class[] locateTestClasses( ClassLoader classLoader)
+        throws TestSetFailedException
+    {
+        String[] testClassNames =   collectTests( );
+        List result = new ArrayList();
+
+        for ( int i = 0; i < testClassNames.length; i++ )
+        {
+            String className = testClassNames[i];
+
+            Class testClass;
+            try
+            {
+                testClass = classLoader.loadClass( className );
+            }
+            catch ( ClassNotFoundException e )
+            {
+                throw new TestSetFailedException( "Unable to create test class '" + className + "'", e );
+            }
+
+            if ( !Modifier.isAbstract( testClass.getModifiers() ) )
+            {
+
+                result.add( testClass);
+            }
+        }
+        return (Class[]) result.toArray(new Class[result.size()]);
+    }
+
+
+    String[] collectTests( )
+    {
+        String[] tests = EMPTY_STRING_ARRAY;
+        if ( basedir.exists() )
+        {
+            org.codehaus.plexus.util.DirectoryScanner scanner = new org.codehaus.plexus.util.DirectoryScanner();
+
+            scanner.setBasedir( basedir );
+
+            if ( includes != null )
+            {
+                scanner.setIncludes( processIncludesExcludes( includes ) );
+            }
+
+            if ( excludes != null )
+            {
+                scanner.setExcludes( processIncludesExcludes( excludes ) );
+            }
+
+            scanner.scan();
+
+            tests = scanner.getIncludedFiles();
+            for ( int i = 0; i < tests.length; i++ )
+            {
+                String test = tests[i];
+                test = test.substring( 0, test.indexOf( "." ) );
+                tests[i] = test.replace( FS.charAt( 0 ), '.' );
+            }
+        }
+        return tests;
+    }
+
+    private static String[] processIncludesExcludes( List list )
+    {
+        String[] incs = new String[list.size()];
+
+        for ( int i = 0; i < incs.length; i++ )
+        {
+            incs[i] = StringUtils.replace( (String) list.get( i ), "java", "class" );
+
+        }
+        return incs;
+    }
+
+
+
+}
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/util/SurefireDirectoryScannerTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/util/SurefireDirectoryScannerTest.java
new file mode 100644
index 0000000..322b680
--- /dev/null
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/util/SurefireDirectoryScannerTest.java
@@ -0,0 +1,45 @@
+package org.apache.maven.surefire.util;
+/*
+ * 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 junit.framework.TestCase;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.maven.surefire.testset.TestSetFailedException;
+
+/**
+ * Test of the directory scanner.
+ */
+public class SurefireDirectoryScannerTest extends TestCase {
+    public void testLocateTestClasses() throws IOException, TestSetFailedException {
+        File baseDir = new File(new File(".").getCanonicalPath());
+        List include = new ArrayList();
+        include.add ("**/*ZT*A.java");
+        List exclude = new ArrayList();
+        
+        SurefireDirectoryScanner surefireDirectoryScanner = new SurefireDirectoryScanner(baseDir, include, exclude);
+        String[] classNames = surefireDirectoryScanner.collectTests();
+        assertNotNull( classNames);
+        assertEquals(3, classNames.length);
+   }
+}
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/util/testdata/DataZT1A.java b/surefire-api/src/test/java/org/apache/maven/surefire/util/testdata/DataZT1A.java
new file mode 100644
index 0000000..691a73e
--- /dev/null
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/util/testdata/DataZT1A.java
@@ -0,0 +1,11 @@
+package org.apache.maven.surefire.util.testdata;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: kristian
+ * Date: Jun 21, 2009
+ * Time: 8:12:27 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class DataZT1A {
+}
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/util/testdata/DataZT2A.java b/surefire-api/src/test/java/org/apache/maven/surefire/util/testdata/DataZT2A.java
new file mode 100644
index 0000000..0e44b75
--- /dev/null
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/util/testdata/DataZT2A.java
@@ -0,0 +1,11 @@
+package org.apache.maven.surefire.util.testdata;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: kristian
+ * Date: Jun 21, 2009
+ * Time: 8:12:27 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class DataZT2A {
+}
\ No newline at end of file
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/util/testdata/DataZT3A.java b/surefire-api/src/test/java/org/apache/maven/surefire/util/testdata/DataZT3A.java
new file mode 100644
index 0000000..05e64a8
--- /dev/null
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/util/testdata/DataZT3A.java
@@ -0,0 +1,11 @@
+package org.apache.maven.surefire.util.testdata;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: kristian
+ * Date: Jun 21, 2009
+ * Time: 8:12:27 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class DataZT3A {
+}
\ No newline at end of file
diff --git a/surefire-integration-tests/pom.xml b/surefire-integration-tests/pom.xml
index 83517ce..1d6ccc8 100644
--- a/surefire-integration-tests/pom.xml
+++ b/surefire-integration-tests/pom.xml
@@ -78,7 +78,7 @@
                <value>${maven.home}</value>
             </property>
           </systemProperties>
-          
+
         </configuration>
       </plugin>
     </plugins>
diff --git a/surefire-providers/pom.xml b/surefire-providers/pom.xml
index bb19ffb..79dfb25 100644
--- a/surefire-providers/pom.xml
+++ b/surefire-providers/pom.xml
@@ -36,6 +36,7 @@
   <modules>
     <module>surefire-junit</module>
     <module>surefire-junit4</module>
+    <module>surefire-junit47</module>
     <module>surefire-testng</module>
   </modules>
 
diff --git a/surefire-providers/surefire-junit47/pom.xml b/surefire-providers/surefire-junit47/pom.xml
new file mode 100644
index 0000000..b43be32
--- /dev/null
+++ b/surefire-providers/surefire-junit47/pom.xml
@@ -0,0 +1,77 @@
+<!--
+  ~ 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>
+
+    <parent>
+        <groupId>org.apache.maven.surefire</groupId>
+        <artifactId>surefire-providers</artifactId>
+        <version>2.5-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>surefire-junit47</artifactId>
+
+    <name>SureFire JUnitCore Runner</name>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.7</version>
+        </dependency>
+        <dependency>
+            <groupId>org.codehaus.plexus</groupId>
+            <artifactId>plexus-utils</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-all</artifactId>
+            <version>1.7</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <fork>false</fork>
+                    <compilerVersion>1.5</compilerVersion>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <jvm>${java.home}/bin/java</jvm>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.5</source>
+                    <target>1.5</target>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/DemultiplexingRunListener.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/DemultiplexingRunListener.java
new file mode 100644
index 0000000..f26c0d3
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/DemultiplexingRunListener.java
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2002-2009 the original author or authors.
+ *
+ * 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.
+ *
+ * Also licensed under CPL http://junit.sourceforge.net/cpl-v10.html
+ */
+package org.apache.maven.surefire.junitcore;
+
+import org.junit.runner.notification.RunListener;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+
+import java.util.*;
+
+/**
+ * Demultiplexes threaded running og tests into something that does not look threaded.
+ * Essentially makes a threaded junit core RunListener behave like something like a
+ * junit4 reporter can handle.
+ * The disadvantage of this demultiplexer is that there's no results being echoed to the
+ * console as tests are being run
+ * All results come like ketchup out of the bottle.
+ *
+ * This class is basically copied from org.jdogma.junit.DemultiplexingRunListener to keep
+ * required external dependencies minimal.
+ * @author Kristian Rosenvold, kristianAzeniorD0Tno
+ */
+class DemultiplexingRunListener extends RunListener {
+    private final Map<String, RecordingRunListener> classList = new HashMap<String, RecordingRunListener>();
+    private final RunListener realtarget;
+
+    public DemultiplexingRunListener(RunListener realtarget) {
+        this.realtarget = realtarget;
+    }
+
+    @Override
+    public void testRunStarted(Description description) throws Exception {
+        // Do nothing. We discard this event because it's basically meaningless
+    }
+
+    @Override
+    public void testRunFinished(Result outerResult) throws Exception {
+        for (RecordingRunListener classReport : classList.values()) {
+            classReport.replay( realtarget);
+        }
+    }
+
+    @Override
+    public void testStarted(Description description) throws Exception {
+        getOrCreateClassReport(description).testStarted( description);
+    }
+
+    @Override
+    public void testFinished(Description description) throws Exception {
+        getClassReport( description).testFinished(description);
+    }
+
+    @Override
+    public void testFailure(Failure failure) throws Exception {
+        getClassReport( failure.getDescription()).testFailure( failure);
+    }
+
+    @Override
+    public void testAssumptionFailure(Failure failure) {
+        getClassReport(failure.getDescription()).testAssumptionFailure( failure);
+    }
+
+    @Override
+    public void testIgnored(Description description) throws Exception {
+        getClassReport(description).testIgnored(description);
+    }
+
+
+    RecordingRunListener getClassReport(Description description) {
+        synchronized ( classList){
+          return classList.get( description.getClassName());
+        }
+    }
+
+    private RecordingRunListener getOrCreateClassReport(Description description) throws Exception {
+        RecordingRunListener result;
+        synchronized (classList) {
+            result = classList.get(description.getClassName());
+            if (result == null) {
+                result = new RecordingRunListener();
+                result.testRunStarted( description);
+                classList.put(description.getClassName(), result);
+            }
+        }
+        return result;
+    }
+
+    public class RecordingRunListener extends RunListener {
+        private volatile Description testRunStarted;
+        private final List<Description> testStarted = Collections.synchronizedList(new ArrayList<Description>());
+        private final List<Description> testFinished =  Collections.synchronizedList(new ArrayList<Description>());
+        private final List<Failure> testFailure =  Collections.synchronizedList(new ArrayList<Failure>());
+        private final List<Failure> testAssumptionFailure =  Collections.synchronizedList(new ArrayList<Failure>());
+        private final List<Description> testIgnored =  Collections.synchronizedList(new ArrayList<Description>());
+        private final Result resultForThisClass = new Result();
+        private final RunListener classRunListener = resultForThisClass.createListener();
+
+
+
+        @Override
+        public void testRunStarted(Description description) throws Exception {
+            this.testRunStarted = description;
+        }
+
+        @Override
+        public void testRunFinished(Result result) throws Exception {
+            throw new IllegalStateException("This method should not be called on the recorder");
+        }
+
+        @Override
+        public void testStarted(Description description) throws Exception {
+            testStarted.add( description);
+            classRunListener.testStarted( description);
+        }
+
+        @Override
+        public void testFinished(Description description) throws Exception {
+            testFinished.add( description);
+            classRunListener.testFinished(description);
+        }
+
+        @Override
+        public void testFailure(Failure failure) throws Exception {
+            testFailure.add( failure);
+            classRunListener.testFailure( failure);
+        }
+
+        @Override
+        public void testAssumptionFailure(Failure failure) {
+            testAssumptionFailure.add( failure);
+        }
+
+        @Override
+        public void testIgnored(Description description) throws Exception {
+            testIgnored.add(  description);
+        }
+
+        public void replay(RunListener target) throws Exception {
+            target.testRunStarted (testRunStarted);
+
+            for( Description description : testStarted) {
+                target.testStarted( description);
+            }
+            for( Failure failure : testFailure) {
+                target.testFailure( failure);
+            }
+            for( Description description : testIgnored) {
+                target.testIgnored( description);
+            }
+            for( Failure failure : testAssumptionFailure) {
+                target.testAssumptionFailure( failure);
+            }
+            for( Description description : testFinished) {
+                target.testFinished( description);
+            }
+            target.testRunFinished( resultForThisClass);
+        }
+
+
+    }
+
+}
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreDirectoryTestSuite.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreDirectoryTestSuite.java
new file mode 100644
index 0000000..a1f4cc5
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreDirectoryTestSuite.java
@@ -0,0 +1,90 @@
+package org.apache.maven.surefire.junitcore;
+
+/*
+ * 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 org.apache.maven.surefire.suite.SurefireTestSuite;
+import org.apache.maven.surefire.testset.TestSetFailedException;
+import org.apache.maven.surefire.report.ReporterManager;
+import org.apache.maven.surefire.report.ReporterException;
+import org.apache.maven.surefire.util.SurefireDirectoryScanner;
+
+import java.util.*;
+import java.io.File;
+
+/**
+ * Test suite for JUnitCore based on a directory of Java test classes.
+ *
+ * @author Karl M. Davis
+ * @author Kristian Rosenvold (junit core adaption)
+ */
+public class JUnitCoreDirectoryTestSuite implements SurefireTestSuite {
+    private final SurefireDirectoryScanner directoryScanner;
+
+    private TestsToRun testsToRun;
+
+    protected Map testSets;
+    private final JUnitCoreParameters jUnitCoreParameters;
+
+
+    public JUnitCoreDirectoryTestSuite(File basedir, ArrayList includes, ArrayList excludes, Properties properties) {
+        directoryScanner = new SurefireDirectoryScanner(basedir, includes, excludes);
+        this.jUnitCoreParameters = new JUnitCoreParameters(properties);
+    }
+
+
+    public void execute(ReporterManager reporterManager, ClassLoader classLoader)
+            throws ReporterException, TestSetFailedException {
+        if (testsToRun == null) {
+            throw new IllegalStateException("You must call locateTestSets before calling execute");
+        }
+
+        JUnitCoreTestSet.execute(testsToRun.getLocatedClasses(), reporterManager, jUnitCoreParameters);
+    }
+
+    public void execute(String testSetName, ReporterManager reporterManager, ClassLoader classLoader)
+            throws ReporterException, TestSetFailedException {
+        if (testsToRun == null) {
+            throw new IllegalStateException("You must call locateTestSets before calling execute");
+        }
+        JUnitCoreTestSet testSet = testsToRun.getTestSet( testSetName);
+
+        if (testSet == null) {
+            throw new TestSetFailedException("Unable to find test set '" + testSetName + "' in suite");
+        }
+        testSet.execute(reporterManager, jUnitCoreParameters);
+    }
+
+    public Map locateTestSets(ClassLoader classLoader) throws TestSetFailedException {
+        if (testSets != null) {
+            throw new IllegalStateException("You can't call locateTestSets twice");
+        }
+
+        Class[] locatedClasses = directoryScanner.locateTestClasses(classLoader);
+        testsToRun = new TestsToRun(locatedClasses);
+        return testsToRun.getTestSets();
+    }
+
+    public int getNumTests() {
+        if (testsToRun == null) {
+            throw new IllegalStateException("You must call locateTestSets before calling getNumTests");
+        }
+        return testsToRun.size();
+    }
+}
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java
new file mode 100644
index 0000000..204da77
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java
@@ -0,0 +1,73 @@
+package org.apache.maven.surefire.junitcore;
+
+import java.util.Properties;
+
+/**
+ * @author Kristian Rosenvold
+ */
+class JUnitCoreParameters {
+
+    private final String parallel;
+    private final Boolean perCoreThreadCount;
+    private final int threadCount;
+    private final Boolean useUnlimitedThreads;
+    private final Boolean configurableParallelComputerPresent;
+    public static final String PARALLEL_KEY = "parallel";
+    public static final String PERCORETHREADCOUNT_KEY = "perCoreThreadCount";
+    public static final String THREADCOUNT_KEY = "threadCount";
+    public static final String USEUNLIMITEDTHREADS_KEY = "useUnlimitedThreads";
+    public static final String CONFIGURABLEPARALLELCOMPUTERPRESENT_KEY = "configurableParallelComputerPresent";
+
+
+    public JUnitCoreParameters(Properties properties) {
+        this.parallel = properties.getProperty(PARALLEL_KEY, "none").toLowerCase();
+        this.perCoreThreadCount = Boolean.valueOf( properties.getProperty(PERCORETHREADCOUNT_KEY, "true"));
+        this.threadCount = Integer.valueOf(properties.getProperty(THREADCOUNT_KEY, "8"));
+        this.useUnlimitedThreads = Boolean.valueOf(properties.getProperty(USEUNLIMITEDTHREADS_KEY, "false").toLowerCase());
+        this.configurableParallelComputerPresent = Boolean.valueOf(properties.getProperty(CONFIGURABLEPARALLELCOMPUTERPRESENT_KEY, "false").toLowerCase());
+    }
+
+    public boolean isParallelMethod(){
+        return "methods".equals( parallel);
+    }
+    public boolean isParallelClasses(){
+        return "classes".equals( parallel);
+    }
+    public boolean isParallelBoth(){
+        return "both".equals( parallel);
+    }
+
+    public Boolean isPerCoreThreadCount() {
+        return perCoreThreadCount;
+    }
+
+    public int getThreadCount() {
+        return threadCount;
+    }
+
+    public Boolean isUseUnlimitedThreads() {
+        return useUnlimitedThreads;
+    }
+
+    public boolean isNoThreading(){
+        return !(isParallelClasses() || isParallelMethod() || isParallelBoth());
+    }
+    public boolean isAnyParallelitySelected(){
+        return !isNoThreading();
+    }
+
+    public Boolean isConfigurableParallelComputerPresent() {
+        return configurableParallelComputerPresent;
+    }
+
+    @Override
+    public String toString() {
+        return "JUnitCoreParameters{" +
+                "parallel='" + parallel + '\'' +
+                ", perCoreThreadCount=" + perCoreThreadCount +
+                ", threadCount=" + threadCount +
+                ", useUnlimitedThreads=" + useUnlimitedThreads +
+                ", configurableParallelComputerPresent=" + configurableParallelComputerPresent +
+                '}';
+    }
+}
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreStackTraceWriter.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreStackTraceWriter.java
new file mode 100644
index 0000000..9ec9b68
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreStackTraceWriter.java
@@ -0,0 +1,78 @@
+package org.apache.maven.surefire.junitcore;
+
+/*
+ * 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 org.apache.maven.surefire.report.StackTraceWriter;
+import org.junit.runner.notification.Failure;
+
+/**
+ * Writes out a specific {@link org.junit.runner.notification.Failure} for
+ * surefire as a stacktrace.
+ *
+ * @author Karl M. Davis
+ * @author Kristian Rosenvold (junit core adaption)
+ */
+class JUnitCoreStackTraceWriter
+    implements StackTraceWriter
+{
+    // Member Variables
+    private Failure junitFailure;
+
+    /**
+     * Constructor.
+     *
+     * @param junitFailure the {@link Failure} that this will be operating on
+     */
+    public JUnitCoreStackTraceWriter( Failure junitFailure )
+    {
+        this.junitFailure = junitFailure;
+    }
+
+    /*
+      * (non-Javadoc)
+      *
+      * @see org.apache.maven.surefire.report.StackTraceWriter#writeTraceToString()
+      */
+    public String writeTraceToString()
+    {
+        return junitFailure.getTrace();
+    }
+
+    /**
+     * At the moment, returns the same as {@link #writeTraceToString()}.
+     *
+     * @see org.apache.maven.surefire.report.StackTraceWriter#writeTrimmedTraceToString()
+     */
+    public String writeTrimmedTraceToString()
+    {
+        return junitFailure.getTrace();
+    }
+
+    /**
+     * Returns the exception associated with this failure.
+     *
+     * @see org.apache.maven.surefire.report.StackTraceWriter#getThrowable()
+     */
+    public Throwable getThrowable()
+    {
+        return junitFailure.getException();
+    }
+
+}
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreTestSet.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreTestSet.java
new file mode 100644
index 0000000..a76a619
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreTestSet.java
@@ -0,0 +1,187 @@
+package org.apache.maven.surefire.junitcore;
+
+/*
+ * 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 org.apache.maven.surefire.report.ReporterManager;
+import org.apache.maven.surefire.testset.TestSetFailedException;
+import org.junit.runner.JUnitCore;
+import org.junit.runner.Computer;
+import org.junit.runner.notification.RunListener;
+import org.junit.experimental.ParallelComputer;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+
+/**
+ * Writes out a specific {@link org.junit.runner.notification.Failure} for
+ * surefire as a stacktrace.
+ *
+ * @author Karl M. Davis
+ * @author Kristian Rosenvold (junit core adaption)
+ */
+
+class JUnitCoreTestSet {
+    private final Class testClass;
+    private static final String className = "org.jdogma.junit.ConfigurableParallelComputer";
+    private static final String demuxerClassName = "org.jdogma.junit.DemultiplexingRunListener";
+
+    public String getName() {
+        return testClass.getName();
+    }
+
+    Class getTestClass() {
+        return testClass;
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param testClasses the classes to be run as a test
+     */
+    protected JUnitCoreTestSet(Class testClasses) {
+        this.testClass = testClasses;
+    }
+
+    /**
+     * Actually runs the test and adds the tests results to the <code>reportManager</code>.
+     *
+     * @param JUnitCoreParameters The parameters for this test
+     * @param reportManager       The report manager
+     * @throws TestSetFailedException If something fails
+     * @see org.apache.maven.surefire.testset.SurefireTestSet#execute(org.apache.maven.surefire.report.ReporterManager,java.lang.ClassLoader)
+     */
+    public void execute(ReporterManager reportManager, JUnitCoreParameters JUnitCoreParameters)
+            throws TestSetFailedException {
+
+        Class[] classes = new Class[1];
+        classes[0] = testClass;
+        execute(classes, reportManager, JUnitCoreParameters);
+    }
+
+    public static void execute(Class[] classes, ReporterManager reportManager, JUnitCoreParameters jUnitCoreParameters)
+            throws TestSetFailedException {
+        RunListener listener = new JUnitCoreTestSetReporter(reportManager);
+        if (jUnitCoreParameters.isAnyParallelitySelected()) {
+            listener = createRunListener(listener, jUnitCoreParameters.isConfigurableParallelComputerPresent());
+        }
+        Computer computer = getComputer(jUnitCoreParameters);
+        try {
+            runJunitCore(classes, computer, listener);
+        } finally {
+            closeIfConfigurable(computer);
+            reportManager.reset();
+        }
+    }
+
+    private static RunListener createRunListener(RunListener realTarget, boolean configurableParallelComputerPresent)
+            throws TestSetFailedException {
+        if (!configurableParallelComputerPresent) return new DemultiplexingRunListener(realTarget);
+        try {
+            Class<?> cpcClass = Class.forName(demuxerClassName);
+            Constructor constructor = cpcClass.getConstructor(RunListener.class);
+            return (RunListener) constructor.newInstance(realTarget);
+        } catch (ClassNotFoundException e) {
+            throw new TestSetFailedException(e);
+        } catch (NoSuchMethodException e) {
+            throw new TestSetFailedException(e);
+        } catch (InvocationTargetException e) {
+            throw new TestSetFailedException(e);
+        } catch (IllegalAccessException e) {
+            throw new TestSetFailedException(e);
+        } catch (InstantiationException e) {
+            throw new TestSetFailedException(e);
+        }
+    }
+
+    private static void closeIfConfigurable(Computer computer) throws TestSetFailedException {
+        if (computer.getClass().getName().startsWith(className)) {
+            try {
+                Class<?> cpcClass = Class.forName(className);
+                Method method = cpcClass.getMethod("close");
+                method.invoke(computer);
+            } catch (ClassNotFoundException e) {
+                throw new TestSetFailedException(e);
+            } catch (NoSuchMethodException e) {
+                throw new TestSetFailedException(e);
+            } catch (InvocationTargetException e) {
+                throw new TestSetFailedException(e);
+            } catch (IllegalAccessException e) {
+                throw new TestSetFailedException(e);
+            }
+        }
+    }
+
+    private static Computer getComputer(JUnitCoreParameters jUnitCoreParameters) throws TestSetFailedException {
+        if (jUnitCoreParameters.isNoThreading()) {
+            return new Computer();
+        }
+        return jUnitCoreParameters.isConfigurableParallelComputerPresent() ?
+                getConfigurableParallelComputer(jUnitCoreParameters) :
+                getParallelComputer(jUnitCoreParameters);
+    }
+
+    private static Computer getParallelComputer(JUnitCoreParameters JUnitCoreParameters) {
+        if (JUnitCoreParameters.isUseUnlimitedThreads())
+            return new ParallelComputer(true, true);
+        else {
+            return new ParallelComputer(JUnitCoreParameters.isParallelClasses(), JUnitCoreParameters.isParallelMethod());
+        }
+    }
+
+    private static Computer getConfigurableParallelComputer(JUnitCoreParameters JUnitCoreParameters) throws TestSetFailedException {
+
+        try {
+            Class<?> cpcClass = Class.forName(className);
+            if (JUnitCoreParameters.isUseUnlimitedThreads()) {
+                Constructor<?> constructor = cpcClass.getConstructor();
+                return (Computer) constructor.newInstance();
+            } else {
+                Constructor<?> constructor = cpcClass.getConstructor(boolean.class, boolean.class, Integer.class, boolean.class);
+                return (Computer) constructor.newInstance(JUnitCoreParameters.isParallelClasses(),
+                        JUnitCoreParameters.isParallelMethod(),
+                        JUnitCoreParameters.getThreadCount(), JUnitCoreParameters.isPerCoreThreadCount());
+            }
+        } catch (ClassNotFoundException e) {
+            throw new TestSetFailedException(e);
+        } catch (NoSuchMethodException e) {
+            throw new TestSetFailedException(e);
+        } catch (InvocationTargetException e) {
+            throw new TestSetFailedException(e);
+        } catch (InstantiationException e) {
+            throw new TestSetFailedException(e);
+        } catch (IllegalAccessException e) {
+            throw new TestSetFailedException(e);
+        }
+    }
+
+    private static void runJunitCore(Class[] classes, Computer computer, RunListener real) throws TestSetFailedException {
+        JUnitCore junitCore = new JUnitCore();
+        junitCore.addListener(real);
+        try {
+            junitCore.run(computer, classes);
+        } finally {
+            junitCore.removeListener(real);
+        }
+    }
+
+
+}
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreTestSetReporter.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreTestSetReporter.java
new file mode 100644
index 0000000..41a3e10
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreTestSetReporter.java
@@ -0,0 +1,154 @@
+package org.apache.maven.surefire.junitcore;
+
+/*
+ * 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.util.ResourceBundle;
+
+import org.apache.maven.surefire.Surefire;
+import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.ReporterManager;
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+
+class JUnitCoreTestSetReporter
+    extends RunListener
+{
+    // Constants
+    private static ResourceBundle bundle = ResourceBundle.getBundle( Surefire.SUREFIRE_BUNDLE_NAME );
+
+
+    private ReporterManager reportMgr;
+
+    /**
+     * This flag is set after a failure has occurred so that a <code>testSucceeded</code> event is not fired.  This is necessary because JUnit4 always fires a <code>testRunFinished</code> event-- even if there was a failure.
+     */
+    private boolean failureFlag;
+
+    /**
+     * Constructor.
+     *
+     * @param reportManager the report manager to log testing events to
+     */
+    JUnitCoreTestSetReporter(ReporterManager reportManager)
+    {
+        this.reportMgr = reportManager;
+    }
+
+    /**
+     * Called right before any tests from a specific class are run.
+     *
+     * @see org.junit.runner.notification.RunListener#testRunStarted(org.junit.runner.Description)
+     */
+    public void testRunStarted( Description description )
+        throws Exception
+    {
+        String rawString = bundle.getString( "testSetStarting" );
+        ReportEntry report = new ReportEntry( description.getClassName(), description.getDisplayName(), rawString );
+
+        this.reportMgr.testSetStarting( report );
+    }
+
+    /**
+     * Called right after all tests from a specific class are run.
+     *
+     * @see org.junit.runner.notification.RunListener#testRunFinished(org.junit.runner.Result)
+     */
+    public void testRunFinished( Result result )
+        throws Exception
+    {
+        String rawString = bundle.getString( "testSetCompletedNormally" );
+        ReportEntry report = new ReportEntry( result.getClass().getCanonicalName(), result.getClass().getName(), rawString );
+        this.reportMgr.testSetCompleted( report );
+        this.reportMgr.reset();
+    }
+
+    /**
+     * Called when a specific test has been skipped (for whatever reason).
+     *
+     * @see org.junit.runner.notification.RunListener#testIgnored(org.junit.runner.Description)
+     */
+    public void testIgnored( Description description )
+        throws Exception
+    {
+        String rawString = bundle.getString( "testSkipped" );
+        ReportEntry report = new ReportEntry( description.getTestClass().getCanonicalName(), description.getDisplayName(), rawString );
+
+        this.reportMgr.testSkipped( report );
+    }
+
+    /**
+     * Called when a specific test has started.
+     *
+     * @see org.junit.runner.notification.RunListener#testStarted(org.junit.runner.Description)
+     */
+    public void testStarted( Description description )
+        throws Exception
+    {
+        String rawString = bundle.getString( "testStarting" );
+        ReportEntry report = new ReportEntry( description.getTestClass().getCanonicalName(), description.getDisplayName(), rawString );
+
+        this.reportMgr.testStarting( report );
+
+        this.failureFlag = false;
+    }
+
+    /**
+     * Called when a specific test has failed.
+     *
+     * @see org.junit.runner.notification.RunListener#testFailure(org.junit.runner.notification.Failure)
+     */
+    public void testFailure( Failure failure )
+        throws Exception
+    {
+        String rawString = bundle.getString( "executeException" );
+        ReportEntry report =
+            new ReportEntry( failure.getDescription().getTestClass().getCanonicalName(), failure.getTestHeader(), rawString, new JUnitCoreStackTraceWriter( failure ) );
+
+        if ( failure.getException() instanceof AssertionError )
+        {
+            this.reportMgr.testFailed( report );
+        }
+        else
+        {
+            this.reportMgr.testError( report );
+        }
+
+        failureFlag = true;
+    }
+
+    /**
+     * Called after a specific test has finished.
+     *
+     * @see org.junit.runner.notification.RunListener#testFinished(org.junit.runner.Description)
+     */
+    public void testFinished( Description description )
+        throws Exception
+    {
+        if ( failureFlag == false )
+        {
+            String rawString = bundle.getString( "testSuccessful" );
+            ReportEntry report = new ReportEntry( description.getTestClass().getCanonicalName(), description.getDisplayName(), rawString );
+
+            this.reportMgr.testSucceeded( report );
+        }
+    }
+}
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestsToRun.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestsToRun.java
new file mode 100644
index 0000000..3793a7c
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestsToRun.java
@@ -0,0 +1,51 @@
+package org.apache.maven.surefire.junitcore;
+
+import org.apache.maven.surefire.testset.TestSetFailedException;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Collections;
+
+/**
+ * Contains all the tests that have been found according to specified include/exclude
+ * specification for a given surefire run.
+ */
+class TestsToRun {
+    final Class[] locatedClasses;
+    final int totalTests;
+    Map<String, JUnitCoreTestSet> testSets;
+
+    public TestsToRun(Class... locatedClasses) throws TestSetFailedException {
+        this.locatedClasses = locatedClasses;
+        testSets = new HashMap<String, JUnitCoreTestSet>();
+        int testCount = 0;
+        for (int i = 0; i < locatedClasses.length; i++) {
+            Class testClass = locatedClasses[i];
+            JUnitCoreTestSet testSet = new JUnitCoreTestSet(testClass);
+
+            if (testSets.containsKey(testSet.getName())) {
+                throw new TestSetFailedException("Duplicate test set '" + testSet.getName() + "'");
+            }
+            testSets.put(testSet.getName(), testSet);
+            testCount++;
+        }
+        this.totalTests = testCount;
+    }
+
+    public Map<String, JUnitCoreTestSet> getTestSets() {
+        return Collections.unmodifiableMap(testSets);
+    }
+
+    public int size() {
+        return testSets.size();
+    }
+
+    public Class[] getLocatedClasses() {
+        return locatedClasses;
+    }
+
+    public JUnitCoreTestSet getTestSet(String name){
+        return testSets.get( name);
+    }
+
+}
diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/DemultiplexingRunListenerTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/DemultiplexingRunListenerTest.java
new file mode 100644
index 0000000..ae87745
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/DemultiplexingRunListenerTest.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2002-2009 the original author or authors.
+ *
+ * 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.
+ *
+ * Also licensed under CPL http://junit.sourceforge.net/cpl-v10.html
+ */
+
+
+package org.apache.maven.surefire.junitcore;
+
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.RunListener;
+import org.junit.Test;
+import static org.mockito.Mockito.*;
+
+/*
+ * @author Kristian Rosenvold, kristian.rosenvold@gmail.com
+ */
+
+public class DemultiplexingRunListenerTest {
+    @Test
+    public void testTestStarted() throws Exception {
+        RunListener real = mock(RunListener.class);
+        DemultiplexingRunListener listener = new DemultiplexingRunListener(real);
+
+        Description testRunDescription = Description.createSuiteDescription(DemultiplexingRunListenerTest.class);
+        Description description1 = Description.createTestDescription( DemultiplexingRunListenerTest.class, "testStub1");
+        Description description2 = Description.createTestDescription( Dummy.class, "testStub2");
+
+        listener.testRunStarted(testRunDescription);
+        listener.testStarted(description1);
+        listener.testStarted(description2);
+        listener.testFinished(description1);
+        listener.testFinished(description2);
+        Result temp = new Result();
+        listener.testRunFinished( temp);
+
+        verify(real).testRunStarted( description1);
+        verify(real).testStarted( description1);
+        verify(real).testRunStarted( description2);
+        verify(real).testStarted( description2);
+    }
+
+    public class Dummy {
+        @Test
+        public void testNotMuch(){
+
+        }
+
+            @Test
+        public void testStub1() {
+            // Add your code here
+        }
+        @Test
+        public void testStub2() {
+            // Add your code here
+        }
+    }
+}
diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreParametersTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreParametersTest.java
new file mode 100644
index 0000000..41b4d17
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/JUnitCoreParametersTest.java
@@ -0,0 +1,120 @@
+
+package org.apache.maven.surefire.junitcore;
+
+
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+import java.util.Properties;
+
+/*
+ * @author Kristian Rosenvold, kristian.rosenvold@gmail.com
+ */
+
+public class JUnitCoreParametersTest {
+    @Test
+    public void testIsParallelMethod() throws Exception {
+        assertFalse( getTestSetClasses().isParallelMethod());
+        assertTrue( getTestSetMethods().isParallelMethod());
+        assertFalse( getTestSetBoth().isParallelMethod());
+    }
+
+    @Test
+    public void testIsParallelClasses() throws Exception {
+        assertTrue( getTestSetClasses().isParallelClasses());
+        assertFalse( getTestSetMethods().isParallelClasses());
+        assertFalse( getTestSetBoth().isParallelClasses());
+    }
+
+    @Test
+    public void testIsParallelBoth() throws Exception {
+        assertFalse( getTestSetClasses().isParallelBoth());
+        assertFalse( getTestSetMethods().isParallelBoth());
+        assertTrue( getTestSetBoth().isParallelBoth());
+    }
+
+    @Test
+    public void testIsPerCoreThreadCount() throws Exception {
+        assertFalse( getTestSetClasses().isPerCoreThreadCount());
+        assertFalse( getTestSetMethods().isPerCoreThreadCount());
+        assertTrue( getTestSetBoth().isPerCoreThreadCount());
+    }
+
+    @Test
+    public void testGetThreadCount() throws Exception {
+        assertFalse( getTestSetClasses().isPerCoreThreadCount());
+        assertFalse( getTestSetMethods().isPerCoreThreadCount());
+        assertTrue( getTestSetBoth().isPerCoreThreadCount());
+    }
+
+    @Test
+    public void testIsUseUnlimitedThreads() throws Exception {
+        assertFalse( getTestSetClasses().isUseUnlimitedThreads());
+        assertTrue( getTestSetMethods().isUseUnlimitedThreads());
+        assertFalse( getTestSetBoth().isUseUnlimitedThreads());
+    }
+
+    @Test
+    public void testIsNoThreading() throws Exception {
+        assertFalse( getTestSetClasses().isNoThreading());
+        assertFalse( getTestSetMethods().isNoThreading());
+        assertFalse( getTestSetBoth().isNoThreading());
+    }
+
+    @Test
+    public void testIsAnyParallelitySelected() throws Exception {
+        assertTrue( getTestSetClasses().isAnyParallelitySelected());
+        assertTrue( getTestSetMethods().isAnyParallelitySelected());
+        assertTrue( getTestSetBoth().isAnyParallelitySelected());
+    }
+
+    @Test
+    public void testIsConfigurableParallelComputerPresent() throws Exception {
+        assertFalse( getTestSetClasses().isConfigurableParallelComputerPresent());
+        assertFalse( getTestSetMethods().isConfigurableParallelComputerPresent());
+        assertTrue( getTestSetBoth().isConfigurableParallelComputerPresent());
+    }
+
+    @Test
+    public void testToString() throws Exception {
+        assertNotNull(getTestSetBoth().toString());
+    }
+
+
+    public Properties getPropsetClasses(){
+        Properties props = new Properties();
+        props.setProperty(JUnitCoreParameters.PARALLEL_KEY,  "classes");
+        props.setProperty(JUnitCoreParameters.PERCORETHREADCOUNT_KEY,  "false");
+        props.setProperty(JUnitCoreParameters.THREADCOUNT_KEY,  "2");
+        props.setProperty(JUnitCoreParameters.USEUNLIMITEDTHREADS_KEY,  "false");
+        props.setProperty(JUnitCoreParameters.CONFIGURABLEPARALLELCOMPUTERPRESENT_KEY,  "false");
+        return props;
+    }
+    public Properties getPropsetMethods(){
+        Properties props = new Properties();
+        props.setProperty(JUnitCoreParameters.PARALLEL_KEY,  "methods");
+        props.setProperty(JUnitCoreParameters.PERCORETHREADCOUNT_KEY,  "false");
+        props.setProperty(JUnitCoreParameters.THREADCOUNT_KEY,  "2");
+        props.setProperty(JUnitCoreParameters.USEUNLIMITEDTHREADS_KEY,  "true");
+        props.setProperty(JUnitCoreParameters.CONFIGURABLEPARALLELCOMPUTERPRESENT_KEY,  "false");
+        return props;
+    }
+    public Properties getPropsetBoth(){
+        Properties props = new Properties();
+        props.setProperty(JUnitCoreParameters.PARALLEL_KEY,  "both");
+        props.setProperty(JUnitCoreParameters.PERCORETHREADCOUNT_KEY,  "true");
+        props.setProperty(JUnitCoreParameters.THREADCOUNT_KEY,  "7");
+        props.setProperty(JUnitCoreParameters.USEUNLIMITEDTHREADS_KEY,  "false");
+        props.setProperty(JUnitCoreParameters.CONFIGURABLEPARALLELCOMPUTERPRESENT_KEY,  "true");
+        return props;
+    }
+    private JUnitCoreParameters getTestSetBoth(){
+      return new JUnitCoreParameters(getPropsetBoth());
+    }
+    private JUnitCoreParameters getTestSetClasses(){
+      return new JUnitCoreParameters(getPropsetClasses());
+    }
+    private JUnitCoreParameters getTestSetMethods(){
+      return new JUnitCoreParameters(getPropsetMethods());
+    }
+}
diff --git a/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/TestsToRunTest.java b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/TestsToRunTest.java
new file mode 100644
index 0000000..a5f80cd
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/test/java/org/apache/maven/surefire/junitcore/TestsToRunTest.java
@@ -0,0 +1,50 @@
+package org.apache.maven.surefire.junitcore;
+
+import org.junit.Test;
+
+/*
+ * Copyright 2002-2009 the original author or authors.
+ *
+ * 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.
+ *
+ * Also licensed under CPL http://junit.sourceforge.net/cpl-v10.html
+ */
+
+
+import static junit.framework.Assert.*;
+
+import java.util.Map;
+
+/*
+ * @author Kristian Rosenvold, kristian.rosenvold@gmail.com
+ */
+
+public class TestsToRunTest {
+    @Test
+    public void testGetTestSets() throws Exception {
+        TestsToRun testsToRun = new TestsToRun(T1.class, T2.class);
+        assertEquals( 2,  testsToRun.size());
+        JUnitCoreTestSet coreTestSet = testsToRun.getTestSet("org.apache.maven.surefire.junitcore.TestsToRunTest$T1");
+        assertNotNull( coreTestSet);
+        assertEquals(T1.class, coreTestSet.getTestClass());
+        Map<String,JUnitCoreTestSet> stringJUnitCoreTestSetMap = testsToRun.getTestSets();
+        assertEquals(2, stringJUnitCoreTestSetMap.size());
+    }
+
+    class T1 {
+        
+    }
+    class T2 {
+
+    }
+}

