MYFACESTEST-19 JUnit4 Runner that runs each Test method in a seperate classloader (thanks to Rudy De Busscher for this patch)
diff --git a/test12/pom.xml b/test12/pom.xml
index 4484902..1838e31 100644
--- a/test12/pom.xml
+++ b/test12/pom.xml
@@ -95,6 +95,14 @@
     </dependencies>
 
     <build>
+        <resources>
+            <resource>
+                <directory>src/main/resources</directory>
+                <includes>
+                    <include>**/*properties</include>
+                </includes>
+            </resource>
+        </resources>
         <plugins>
             <plugin>
                 <artifactId>maven-compiler-plugin</artifactId>
diff --git a/test12/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java b/test12/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java
index fbe30cb..54e444e 100644
--- a/test12/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java
+++ b/test12/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java
@@ -202,7 +202,9 @@
         application = null;
         config = null;
         externalContext = null;
-        facesContext.release();
+        if (facesContext != null) {
+            facesContext.release();
+        }
         facesContext = null;
         lifecycle = null;
         lifecycleFactory = null;
diff --git a/test12/src/main/java/org/apache/myfaces/test/runners/NamedRunner.java b/test12/src/main/java/org/apache/myfaces/test/runners/NamedRunner.java
new file mode 100644
index 0000000..5493027
--- /dev/null
+++ b/test12/src/main/java/org/apache/myfaces/test/runners/NamedRunner.java
@@ -0,0 +1,123 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to you under the Apache License, Version 2.0

+ * (the "License"); you may not use this file except in compliance with

+ * the License.  You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.apache.myfaces.test.runners;

+

+import org.junit.runner.notification.RunNotifier;

+import org.junit.runners.BlockJUnit4ClassRunner;

+import org.junit.runners.model.FrameworkMethod;

+import org.junit.runners.model.InitializationError;

+

+/**

+ * A Junit 4 runner that stores the name of the method that runs.  The methods marked with @After and @Before 

+ * (but also the Test method itself) can request this name for which it is running.

+ * 

+ * @author Rudy De Busscher

+ */

+public class NamedRunner extends BlockJUnit4ClassRunner

+{

+

+    /** The Constant PREFIX_KEY. */

+    private static final String PREFIX_KEY = "ClassLoaderRunner_TestMethodName_";

+

+    /** The Constant NO_NAME. */

+    private static final String NO_NAME = "null";

+

+    /**

+     * Instantiates a new named runner.

+     * 

+     * @param klass the klass

+     * 

+     * @throws InitializationError the initialization error

+     */

+    public NamedRunner(Class<?> klass) throws InitializationError

+    {

+        super(klass);

+    }

+

+    @Override

+    protected void runChild(FrameworkMethod method, RunNotifier notifier)

+

+    {

+        storeTestMethodName(method.getName());

+        super.runChild(method, notifier);

+        removeTestMethodName();

+    }

+

+    /**

+     * Gets the test method name.

+     * 

+     * @return the test method name

+     */

+    public static String getTestMethodName()

+    {

+        return retrieveTestMethodName();

+    }

+

+    /**

+     * Retrieve test method name.

+     * 

+     * @return the string

+     */

+    private static String retrieveTestMethodName()

+    {

+        // We can't use a ThreadLocal variable in the case the TestPerClassLoader runner is used.  Then this

+        // Method is accessed from another classloader and thus reinitialized variables.

+        String result = null;

+        String storedName = System.getProperty(getKey());

+        if (!NO_NAME.equals(storedName))

+        {

+            result = storedName;

+        }

+        return result;

+    }

+

+    /**

+     * Removes the test method name.

+     */

+    private static void removeTestMethodName()

+    {

+        // We can't use a ThreadLocal variable in the case the TestPerClassLoader runner is used.  Then this

+        // Method is accessed from another classloader and thus reinitialized variables.

+        System.setProperty(getKey(), NO_NAME);

+

+    }

+

+    /**

+     * Store test method name.

+     * 

+     * @param name the name

+     */

+    private static void storeTestMethodName(String name)

+    {

+

+        // We can't use a ThreadLocal variable in the case the TestPerClassLoader runner is used.  Then this

+        // Method is accessed from another classloader and thus reinitialized variables.

+        System.setProperty(getKey(), name);

+    }

+

+    /**

+     * Gets the key.

+     * 

+     * @return the key

+     */

+    private static String getKey()

+    {

+        StringBuffer buffer = new StringBuffer();

+        buffer.append(PREFIX_KEY).append(Thread.currentThread().getName());

+        return buffer.toString();

+    }

+}

diff --git a/test12/src/main/java/org/apache/myfaces/test/runners/TestClassLoader.java b/test12/src/main/java/org/apache/myfaces/test/runners/TestClassLoader.java
new file mode 100644
index 0000000..036f1f0
--- /dev/null
+++ b/test12/src/main/java/org/apache/myfaces/test/runners/TestClassLoader.java
@@ -0,0 +1,332 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to you under the Apache License, Version 2.0

+ * (the "License"); you may not use this file except in compliance with

+ * the License.  You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.apache.myfaces.test.runners;

+

+import java.io.ByteArrayOutputStream;

+import java.io.File;

+import java.io.FileInputStream;

+import java.io.IOException;

+import java.io.InputStream;

+import java.net.URL;

+import java.util.Enumeration;

+import java.util.Properties;

+import java.util.StringTokenizer;

+import java.util.Vector;

+import java.util.zip.ZipEntry;

+import java.util.zip.ZipFile;

+

+/**

+ * A Classloader that delegates the loading of the classes to the parent if it is in the 

+ * excluded list or does it itself otherwise.

+ * Useful for a system that sets up a classloader per Test system.

+ *  

+ * @author Rudy De Busscher

+ */

+public class TestClassLoader extends ClassLoader

+{

+    /** scanned class path */

+    private Vector<String> fPathItems;

+    /** default excluded paths */

+    private String[] defaultExclusions = { "junit.framework.",

+            "junit.extensions.", "junit.runner." };

+    /** name of excluded properties file */

+    static final String EXCLUDED_FILE = "excluded.properties";

+    /** excluded paths */

+    private Vector<String> fExcluded;

+

+    /**

+     * Constructs a TestCaseLoader. It scans the class path

+     * and the excluded package paths

+     */

+    public TestClassLoader()

+    {

+        this(System.getProperty("java.class.path"));

+    }

+

+    /**

+     * Constructs a TestCaseLoader. It scans the class path

+     * and the excluded package paths

+     */

+    public TestClassLoader(String classPath)

+    {

+        scanPath(classPath);

+        readExcludedPackages();

+    }

+

+    private void scanPath(String classPath)

+    {

+        String separator = System.getProperty("path.separator");

+        fPathItems = new Vector<String>(10);

+        StringTokenizer st = new StringTokenizer(classPath, separator);

+        while (st.hasMoreTokens())

+        {

+            fPathItems.addElement(st.nextToken());

+        }

+    }

+

+    @Override

+    public URL getResource(String name)

+    {

+        return ClassLoader.getSystemResource(name);

+    }

+

+    @Override

+    public InputStream getResourceAsStream(String name)

+    {

+        return ClassLoader.getSystemResourceAsStream(name);

+    }

+

+    /**

+     * Checks if path is excluded.

+     * 

+     * @param name the name

+     * 

+     * @return true, if is excluded

+     */

+    public boolean isExcluded(String name)

+    {

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

+        {

+            if (name.startsWith((String) fExcluded.elementAt(i)))

+            {

+                return true;

+            }

+        }

+        return false;

+    }

+

+    @Override

+    public synchronized Class loadClass(String name, boolean resolve)

+            throws ClassNotFoundException

+    {

+

+        Class c = findLoadedClass(name);

+        if (c != null)

+            return c;

+        //

+        // Delegate the loading of excluded classes to the

+        // standard class loader.

+        //

+        if (isExcluded(name))

+        {

+            try

+            {

+                c = findSystemClass(name);

+                return c;

+            }

+            catch (ClassNotFoundException e)

+            {

+                // keep searching

+            }

+        }

+        if (c == null)

+        {

+            byte[] data = lookupClassData(name);

+            if (data == null)

+                throw new ClassNotFoundException();

+            c = defineClass(name, data, 0, data.length);

+        }

+        if (resolve)

+            resolveClass(c);

+        return c;

+    }

+

+    /**

+     * Lookup class data in one of the classpath elements configured.

+     * 

+     * @param className the class name

+     * 

+     * @return the bytes making up the class

+     * 

+     * @throws ClassNotFoundException when class can't be found on the classpath elements.

+     */

+    private byte[] lookupClassData(String className)

+            throws ClassNotFoundException

+    {

+        byte[] data = null;

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

+        {

+            String path = (String) fPathItems.elementAt(i);

+            String fileName = className.replace('.', '/') + ".class";

+            if (isJar(path))

+            {

+                data = loadJarData(path, fileName);

+            }

+            else

+            {

+                data = loadFileData(path, fileName);

+            }

+            if (data != null)

+                return data;

+        }

+        throw new ClassNotFoundException(className);

+    }

+

+    /**

+     * Checks if is jar.

+     * 

+     * @param pathEntry the path entry

+     * 

+     * @return true, if is jar

+     */

+    boolean isJar(String pathEntry)

+    {

+        return pathEntry.endsWith(".jar") || pathEntry.endsWith(".zip");

+    }

+

+    /**

+     * Load class bytes from file.

+     * 

+     * @param path the path

+     * @param fileName the file name

+     * 

+     * @return the bytes making up the class

+     */

+    private byte[] loadFileData(String path, String fileName)

+    {

+        File file = new File(path, fileName);

+        if (file.exists())

+        {

+            return getClassData(file);

+        }

+        return null;

+    }

+

+    private byte[] getClassData(File f)

+    {

+        try

+        {

+            FileInputStream stream = new FileInputStream(f);

+            ByteArrayOutputStream out = new ByteArrayOutputStream(1000);

+            byte[] b = new byte[1000];

+            int n;

+            while ((n = stream.read(b)) != -1)

+                out.write(b, 0, n);

+            stream.close();

+            out.close();

+            return out.toByteArray();

+

+        }

+        catch (IOException e)

+        {

+        }

+        return null;

+    }

+

+    /**

+     * Load class bytes from jar.

+     * 

+     * @param path the path

+     * @param fileName the file name

+     * 

+     * @return the bytes making up the class

+     */

+    private byte[] loadJarData(String path, String fileName)

+    {

+        ZipFile zipFile = null;

+        InputStream stream = null;

+        File archive = new File(path);

+        if (!archive.exists())

+            return null;

+        try

+        {

+            zipFile = new ZipFile(archive);

+        }

+        catch (IOException io)

+        {

+            return null;

+        }

+        ZipEntry entry = zipFile.getEntry(fileName);

+        if (entry == null)

+            return null;

+        int size = (int) entry.getSize();

+        try

+        {

+            stream = zipFile.getInputStream(entry);

+            byte[] data = new byte[size];

+            int pos = 0;

+            while (pos < size)

+            {

+                int n = stream.read(data, pos, data.length - pos);

+                pos += n;

+            }

+            zipFile.close();

+            return data;

+        }

+        catch (IOException e)

+        {

+        }

+        finally

+        {

+            try

+            {

+                if (stream != null)

+                    stream.close();

+            }

+            catch (IOException e)

+            {

+            }

+        }

+        return null;

+    }

+

+    /**

+     * Read excluded packages for which classes are read by the parent class loader.

+     */

+    private void readExcludedPackages()

+    {

+        fExcluded = new Vector<String>(10);

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

+            fExcluded.addElement(defaultExclusions[i]);

+

+        InputStream is = getClass().getResourceAsStream(EXCLUDED_FILE);

+        if (is == null)

+            return;

+        Properties p = new Properties();

+        try

+        {

+            p.load(is);

+        }

+        catch (IOException e)

+        {

+            return;

+        }

+        finally

+        {

+            try

+            {

+                is.close();

+            }

+            catch (IOException e)

+            {

+            }

+        }

+        for (Enumeration e = p.propertyNames(); e.hasMoreElements();)

+        {

+            String key = (String) e.nextElement();

+            if (key.startsWith("excluded."))

+            {

+                String path = p.getProperty(key);

+                path = path.trim();

+                if (path.endsWith("*"))

+                    path = path.substring(0, path.length() - 1);

+                if (path.length() > 0)

+                    fExcluded.addElement(path);

+            }

+        }

+    }

+}

diff --git a/test12/src/main/java/org/apache/myfaces/test/runners/TestPerClassLoaderRunner.java b/test12/src/main/java/org/apache/myfaces/test/runners/TestPerClassLoaderRunner.java
new file mode 100644
index 0000000..32e2c12
--- /dev/null
+++ b/test12/src/main/java/org/apache/myfaces/test/runners/TestPerClassLoaderRunner.java
@@ -0,0 +1,293 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to you under the Apache License, Version 2.0

+ * (the "License"); you may not use this file except in compliance with

+ * the License.  You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.apache.myfaces.test.runners;

+

+import java.io.File;

+import java.io.IOException;

+import java.lang.annotation.Annotation;

+import java.lang.reflect.Method;

+import java.net.MalformedURLException;

+import java.util.List;

+import java.util.StringTokenizer;

+import java.util.Vector;

+import java.util.jar.Attributes;

+import java.util.jar.Manifest;

+import java.util.logging.Logger;

+import java.util.zip.ZipEntry;

+import java.util.zip.ZipFile;

+

+import org.junit.After;

+import org.junit.Before;

+import org.junit.internal.runners.statements.Fail;

+import org.junit.internal.runners.statements.RunAfters;

+import org.junit.internal.runners.statements.RunBefores;

+import org.junit.runners.model.FrameworkMethod;

+import org.junit.runners.model.InitializationError;

+import org.junit.runners.model.Statement;

+import org.junit.runners.model.TestClass;

+

+/**

+ * A Junit 4 runner that executes each Test method with a new Custom classloader so that all variables, 

+ * also the final ones, are reinitialized.

+ * 

+ * @author Rudy De Busscher

+

+ */

+public class TestPerClassLoaderRunner extends NamedRunner

+{

+    private static final Logger LOGGER = Logger

+            .getLogger(TestPerClassLoaderRunner.class.getName());

+

+    // The classpath is needed because the custom class loader looks there to find the classes.

+    private static String classPath;

+    private static boolean classPathDetermined = false;

+

+    // Some data related to the class from the custom class loader.

+    private TestClass testClassFromClassLoader;

+    private Object beforeFromClassLoader;

+    private Object afterFromClassLoader;

+

+    /**

+     * Instantiates a new test per class loader runner.

+     * 

+     * @param klass the klass

+     * 

+     * @throws InitializationError the initialization error

+     */

+    public TestPerClassLoaderRunner(Class<?> klass) throws InitializationError

+    {

+        super(klass);

+    }

+

+    @Override

+    protected Object createTest() throws Exception

+    {

+        // Need an instance now from the class loaded by the custom loader.

+        return testClassFromClassLoader.getJavaClass().newInstance();

+    }

+

+    /**

+     * Load classes (TestCase, @Before and @After with custom class loader.

+     * 

+     * @throws ClassNotFoundException the class not found exception

+     */

+    private void loadClassesWithCustomClassLoader()

+            throws ClassNotFoundException

+    {

+        ClassLoader classLoader;

+

+        // We need the classpath so that our custom loader can search for the requested classes.

+        String testPath = getClassPath();

+        if (testPath != null)

+        {

+            classLoader = new TestClassLoader(testPath);

+        }

+

+        else

+        {

+            classLoader = new TestClassLoader();

+

+        }

+

+        testClassFromClassLoader = new TestClass(classLoader

+                .loadClass(getTestClass().getJavaClass().getName()));

+        // See withAfters and withBefores for the reason.

+        beforeFromClassLoader = classLoader.loadClass(Before.class.getName());

+        afterFromClassLoader = classLoader.loadClass(After.class.getName());

+    }

+

+    @Override

+    protected Statement methodBlock(FrameworkMethod method)

+    {

+        FrameworkMethod newMethod = null;

+        try

+        {

+            // Need the class frmo the custom loader now, so lets load the class.

+            loadClassesWithCustomClassLoader();

+            // The method as parameter is frmo the original class and thus not found in our

+            // class loaded by the custom name (reflection is class loader sensitive)

+            // So find the same method but now in the class frmo the class Loader.

+            Method methodFromNewlyLoadedClass = testClassFromClassLoader

+                    .getJavaClass().getMethod(method.getName());

+            newMethod = new FrameworkMethod(methodFromNewlyLoadedClass);

+        }

+        catch (ClassNotFoundException e)

+        {

+            // Show any problem nicely as a JUnit Test failure.

+            return new Fail(e);

+        }

+        catch (SecurityException e)

+        {

+            return new Fail(e);

+        }

+        catch (NoSuchMethodException e)

+        {

+            return new Fail(e);

+        }

+

+        // We can carry out the normal JUnit functionality with our newly discoverd method now. 

+        return super.methodBlock(newMethod);

+    }

+

+    @SuppressWarnings("unchecked")

+    @Override

+    protected Statement withAfters(FrameworkMethod method, Object target,

+            Statement statement)

+    {

+        // We now to need to search in the class from the custom loader.

+        //We also need to search with the annotation loaded by the custom class loader or otherwise we don't find any method.

+        List<FrameworkMethod> afters = testClassFromClassLoader

+                .getAnnotatedMethods((Class<? extends Annotation>) afterFromClassLoader);

+        return new RunAfters(statement, afters, target);

+    }

+

+    @SuppressWarnings("unchecked")

+    @Override

+    protected Statement withBefores(FrameworkMethod method, Object target,

+            Statement statement)

+    {

+        // We now to need to search in the class from the custom loader.

+        //We also need to search with the annotation loaded by the custom class loader or otherwise we don't find any method.

+        List<FrameworkMethod> befores = testClassFromClassLoader

+                .getAnnotatedMethods((Class<? extends Annotation>) beforeFromClassLoader);

+        return new RunBefores(statement, befores, target);

+    }

+

+    /**

+     * Gets the class path. This value is cached in a static variable for performance reasons.

+     * 

+     * @return the class path

+     */

+    private static String getClassPath()

+    {

+        if (classPathDetermined)

+        {

+            return classPath;

+        }

+

+        classPathDetermined = true;

+        // running from maven, we have the classpath in this property.

+        classPath = System.getProperty("surefire.test.class.path");

+        if (classPath != null)

+        {

+            return classPath;

+        }

+

+        // For a multi module project, running it from the top we have to find it using another way.

+        // We also need to set useSystemClassLoader=true in the POM so that we gets a jar with the classpath in it.

+        String booterClassPath = System.getProperty("java.class.path");

+        Vector<String> pathItems = null;

+        if (booterClassPath != null)

+        {

+            pathItems = scanPath(booterClassPath);

+        }

+        // Do we have just 1 entry as classpath which is a jar?

+        if (pathItems != null && pathItems.size() == 1

+                && isJar((String) pathItems.get(0)))

+        {

+            classPath = loadJarManifestClassPath((String) pathItems.get(0),

+                    "META-INF/MANIFEST.MF");

+        }

+        return classPath;

+

+    }

+

+    /**

+     * Load jar manifest class path.

+     * 

+     * @param path the path

+     * @param fileName the file name

+     * 

+     * @return the string

+     */

+    private static String loadJarManifestClassPath(String path, String fileName)

+    {

+        File archive = new File(path);

+        if (!archive.exists())

+        {

+            return null;

+        }

+        ZipFile zipFile = null;

+

+        try

+        {

+            zipFile = new ZipFile(archive);

+        }

+        catch (IOException io)

+        {

+            return null;

+        }

+

+        ZipEntry entry = zipFile.getEntry(fileName);

+        if (entry == null)

+        {

+            return null;

+        }

+        try

+        {

+            Manifest mf = new Manifest();

+            mf.read(zipFile.getInputStream(entry));

+

+            return mf.getMainAttributes().getValue(Attributes.Name.CLASS_PATH)

+                    .replaceAll(" ", System.getProperty("path.separator"))

+                    .replaceAll("file:/", "");

+        }

+        catch (MalformedURLException e)

+        {

+            LOGGER.throwing("ClassLoaderTestSuite", "loadJarManifestClassPath",

+                    e);

+        }

+        catch (IOException e)

+        {

+            LOGGER.throwing("ClassLoaderTestSuite", "loadJarManifestClassPath",

+                    e);

+        }

+        return null;

+    }

+

+    /**

+     * Checks if is jar.

+     * 

+     * @param pathEntry the path entry

+     * 

+     * @return true, if is jar

+     */

+    private static boolean isJar(String pathEntry)

+    {

+        return pathEntry.endsWith(".jar") || pathEntry.endsWith(".zip");

+    }

+

+    /**

+     * Scan path for all directories.

+     * 

+     * @param classPath the class path

+     * 

+     * @return the vector< string>

+     */

+    private static Vector<String> scanPath(String classPath)

+    {

+        String separator = System.getProperty("path.separator");

+        Vector<String> pathItems = new Vector<String>(10);

+        StringTokenizer st = new StringTokenizer(classPath, separator);

+        while (st.hasMoreTokens())

+        {

+            pathItems.addElement(st.nextToken());

+        }

+        return pathItems;

+    }

+

+}

diff --git a/test12/src/main/resources/org/apache/myfaces/test/runners/excluded.properties b/test12/src/main/resources/org/apache/myfaces/test/runners/excluded.properties
new file mode 100644
index 0000000..a936e9a
--- /dev/null
+++ b/test12/src/main/resources/org/apache/myfaces/test/runners/excluded.properties
@@ -0,0 +1,12 @@
+#

+# The list of excluded package paths for the TestCaseClassLoader

+#

+excluded.0=sun.*

+excluded.1=com.sun.*

+excluded.2=org.omg.*

+excluded.3=javax.*

+excluded.4=sunw.*

+excluded.5=java.*

+excluded.6=org.w3c.dom.*

+excluded.7=org.xml.sax.*

+excluded.8=net.jini.*

diff --git a/test12/src/test/java/org/apache/myfaces/test/runner/TestPerCLassLoaderDefaultTestCase.java b/test12/src/test/java/org/apache/myfaces/test/runner/TestPerCLassLoaderDefaultTestCase.java
new file mode 100644
index 0000000..9106b8d
--- /dev/null
+++ b/test12/src/test/java/org/apache/myfaces/test/runner/TestPerCLassLoaderDefaultTestCase.java
@@ -0,0 +1,71 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to you under the Apache License, Version 2.0

+ * (the "License"); you may not use this file except in compliance with

+ * the License.  You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.apache.myfaces.test.runner;

+

+import junit.framework.Assert;

+

+import org.apache.myfaces.test.base.junit4.AbstractJsfConfigurableMockTestCase;

+import org.apache.myfaces.test.runners.NamedRunner;

+import org.junit.Test;

+import org.junit.runner.RunWith;

+

+/**

+ * 

+ * @author Rudy De Busscher

+ */

+@RunWith(value = NamedRunner.class)

+public class TestPerCLassLoaderDefaultTestCase extends

+        AbstractJsfConfigurableMockTestCase

+{

+

+    @Override

+    protected void setUpExternalContext() throws Exception

+    {

+

+        super.setUpExternalContext();

+

+        if (needXmlParameters())

+        {

+            servletContext

+                    .addInitParameter(WebXmlParameter.PARAMETER_KEY, "60");

+        }

+

+    }

+

+    @Test

+    public void testGetParameterDefault()

+    {

+        Assert.assertNull(WebXmlParameter.PARAMETER);

+    }

+

+    @Test

+    public void testGetParameterWebXml()

+    {

+

+        Assert.assertNull(WebXmlParameter.PARAMETER);

+        // Although the parameter is set, the final variable keeps it value

+        // Since we aren't running with the TestPerClassLoader Test case.

+        Assert.assertNotNull(servletContext

+                .getInitParameter(WebXmlParameter.PARAMETER_KEY));

+    }

+

+    // These methods can be placed in a common super class.

+    protected boolean needXmlParameters()

+    {

+        return NamedRunner.getTestMethodName().contains("Xml");

+    }

+}

diff --git a/test12/src/test/java/org/apache/myfaces/test/runner/TestPerClassLoaderTestCase.java b/test12/src/test/java/org/apache/myfaces/test/runner/TestPerClassLoaderTestCase.java
new file mode 100644
index 0000000..86fe9ed
--- /dev/null
+++ b/test12/src/test/java/org/apache/myfaces/test/runner/TestPerClassLoaderTestCase.java
@@ -0,0 +1,66 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to you under the Apache License, Version 2.0

+ * (the "License"); you may not use this file except in compliance with

+ * the License.  You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.apache.myfaces.test.runner;

+

+import junit.framework.Assert;

+

+import org.apache.myfaces.test.base.junit4.AbstractJsfConfigurableMockTestCase;

+import org.apache.myfaces.test.runners.TestPerClassLoaderRunner;

+import org.junit.Test;

+import org.junit.runner.RunWith;

+

+/**

+ * 

+ * @author Rudy De Busscher

+ */

+@RunWith(value = TestPerClassLoaderRunner.class)

+public class TestPerClassLoaderTestCase extends

+        AbstractJsfConfigurableMockTestCase

+{

+

+    @Override

+    protected void setUpExternalContext() throws Exception

+    {

+

+        super.setUpExternalContext();

+

+        if (needXmlParameters())

+        {

+            servletContext

+                    .addInitParameter(WebXmlParameter.PARAMETER_KEY, "60");

+        }

+

+    }

+

+    @Test

+    public void testGetParameterDefault()

+    {

+        Assert.assertNull(WebXmlParameter.PARAMETER);

+    }

+

+    @Test

+    public void testGetParameterWebXml()

+    {

+        Assert.assertEquals("60", WebXmlParameter.PARAMETER);

+    }

+

+    // These methods can be placed in a common super class.

+    protected boolean needXmlParameters()

+    {

+        return TestPerClassLoaderRunner.getTestMethodName().contains("Xml");

+    }

+}

diff --git a/test12/src/test/java/org/apache/myfaces/test/runner/WebXmlParameter.java b/test12/src/test/java/org/apache/myfaces/test/runner/WebXmlParameter.java
new file mode 100644
index 0000000..455fb58
--- /dev/null
+++ b/test12/src/test/java/org/apache/myfaces/test/runner/WebXmlParameter.java
@@ -0,0 +1,30 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to you under the Apache License, Version 2.0

+ * (the "License"); you may not use this file except in compliance with

+ * the License.  You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.apache.myfaces.test.runner;

+

+/**

+ * 

+ * @author Rudy De Busscher

+ */

+public class WebXmlParameter

+{

+    public static final String PARAMETER_KEY = "PARAMETER";

+    

+    static final String PARAMETER = WebXmlUtils

+            .getInitParameter(PARAMETER_KEY);

+

+}

diff --git a/test12/src/test/java/org/apache/myfaces/test/runner/WebXmlUtils.java b/test12/src/test/java/org/apache/myfaces/test/runner/WebXmlUtils.java
new file mode 100644
index 0000000..f122dfc
--- /dev/null
+++ b/test12/src/test/java/org/apache/myfaces/test/runner/WebXmlUtils.java
@@ -0,0 +1,40 @@
+/*

+ * Licensed to the Apache Software Foundation (ASF) under one or more

+ * contributor license agreements.  See the NOTICE file distributed with

+ * this work for additional information regarding copyright ownership.

+ * The ASF licenses this file to you under the Apache License, Version 2.0

+ * (the "License"); you may not use this file except in compliance with

+ * the License.  You may obtain a copy of the License at

+ *

+ *      http://www.apache.org/licenses/LICENSE-2.0

+ *

+ * Unless required by applicable law or agreed to in writing, software

+ * distributed under the License is distributed on an "AS IS" BASIS,

+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

+ * See the License for the specific language governing permissions and

+ * limitations under the License.

+ */

+package org.apache.myfaces.test.runner;

+

+import javax.faces.context.FacesContext;

+

+/**

+ * 

+ * @author Rudy De Busscher

+ */

+public final class WebXmlUtils

+{

+

+    private WebXmlUtils()

+    {

+

+    }

+

+    public static String getInitParameter(String parameterName)

+    {

+        String value = FacesContext.getCurrentInstance().getExternalContext()

+                .getInitParameter(parameterName);

+        return (value != null) ? value.replace(" ", "").trim() : null;

+    }

+

+}