Package org.apache.myfaces.test.runners

Source Code of org.apache.myfaces.test.runners.TestPerClassLoaderRunner

/*
* 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
* @since 1.0.0
*/
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();

        }

        Thread.currentThread().setContextClassLoader(classLoader);
        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;
    }

}
TOP

Related Classes of org.apache.myfaces.test.runners.TestPerClassLoaderRunner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.