Package org.mockito.internal.configuration

Source Code of org.mockito.internal.configuration.ClassPathLoader

/*
* Copyright (c) 2007 Mockito contributors
* This program is made available under the terms of the MIT License.
*/
package org.mockito.internal.configuration;

import org.mockito.configuration.IMockitoConfiguration;
import org.mockito.exceptions.base.MockitoException;
import org.mockito.exceptions.misusing.MockitoConfigurationException;
import org.mockito.plugins.MockMaker;
import org.mockito.plugins.StackTraceCleanerProvider;

import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;

/**
* Loads configuration or extension points available in the classpath.
*
* <p>
* <ul>
*     <li>
*         Can load the mockito configuration. The user who want to provide his own mockito configuration
*         should write the class <code>org.mockito.configuration.MockitoConfiguration</code> that implements
*         {@link IMockitoConfiguration}. For example :
*         <pre class="code"><code class="java">
* package org.mockito.configuration;
*
* //...
*
* public class MockitoConfiguration implements IMockitoConfiguration {
*     boolean enableClassCache() { return false; }
*
*     // ...
* }
*     </code></pre>
*     </li>
*     <li>
*         Can load available mockito extensions. Currently Mockito only have one extension point the
*         {@link MockMaker}. This extension point allows a user to provide his own bytecode engine to build mocks.
*         <br>Suppose you wrote an extension to create mocks with some <em>Awesome</em> library, in order to tell
*         Mockito to use it you need to put in your classpath
*         <ol style="list-style-type: lower-alpha">
*             <li>The implementation itself, for example <code>org.awesome.mockito.AwesomeMockMaker</code>.</li>
*             <li>A file named <code>org.mockito.plugins.MockMaker</code> in a folder named
*             <code>mockito-extensions</code>, the content of this file need to have <strong>one</strong> line with
*             the qualified name <code>org.awesome.mockito.AwesomeMockMaker</code>.</li>
*         </ol>
*     </li>
* </ul>
* </p>
*/
public class ClassPathLoader {
    private static final String DEFAULT_MOCK_MAKER_CLASS =
            "org.mockito.internal.creation.cglib.CglibMockMaker";
    private static final String DEFAULT_STACK_TRACE_CLEANER_PROVIDER_CLASS =
            "org.mockito.internal.exceptions.stacktrace.DefaultStackTraceCleanerProvider";
    public static final String MOCKITO_CONFIGURATION_CLASS_NAME = "org.mockito.configuration.MockitoConfiguration";

    private static final MockMaker mockMaker = findPlatformMockMaker();
    private static final StackTraceCleanerProvider stackTraceCleanerProvider =
            findPlatformStackTraceCleanerProvider();

    /**
     * @return configuration loaded from classpath or null
     */
    @SuppressWarnings({"unchecked"})
    public IMockitoConfiguration loadConfiguration() {
        //Trying to get config from classpath
        Class configClass;
        try {
            configClass = (Class) Class.forName(MOCKITO_CONFIGURATION_CLASS_NAME);
        } catch (ClassNotFoundException e) {
            //that's ok, it means there is no global config, using default one.
            return null;
        }

        try {
            return (IMockitoConfiguration) configClass.newInstance();
        } catch (ClassCastException e) {
            throw new MockitoConfigurationException("MockitoConfiguration class must implement " + IMockitoConfiguration.class.getName() + " interface.", e);
        } catch (Exception e) {
            throw new MockitoConfigurationException("Unable to instantiate " + MOCKITO_CONFIGURATION_CLASS_NAME +" class. Does it have a safe, no-arg constructor?", e);
        }
    }

    /**
     * Returns the implementation of the mock maker available for the current runtime.
     *
     * <p>Returns {@link org.mockito.internal.creation.cglib.CglibMockMaker} if no
     * {@link MockMaker} extension exists or is visible in the current classpath.</p>
     */
    public static MockMaker getMockMaker() {
        return mockMaker;
    }

    public static StackTraceCleanerProvider getStackTraceCleanerProvider() {
        //TODO we should throw some sensible exception if this is null.
        return stackTraceCleanerProvider;
    }

    /**
     * Scans the classpath to find a mock maker plugin if one is available,
     * allowing mockito to run on alternative platforms like Android.
     */
    static MockMaker findPlatformMockMaker() {
        return findPluginImplementation(MockMaker.class, DEFAULT_MOCK_MAKER_CLASS);
    }

    static StackTraceCleanerProvider findPlatformStackTraceCleanerProvider() {
        return findPluginImplementation(
                StackTraceCleanerProvider.class, DEFAULT_STACK_TRACE_CLEANER_PROVIDER_CLASS);
    }

    static <T> T findPluginImplementation(Class<T> pluginType, String defaultPluginClassName) {
        for (T plugin : loadImplementations(pluginType)) {
            return plugin; // return the first one service loader finds (if any)
        }

        try {
            // Default implementation. Use our own ClassLoader instead of the context
            // ClassLoader, as the default implementation is assumed to be part of
            // Mockito and may not be available via the context ClassLoader.
            return pluginType.cast(Class.forName(defaultPluginClassName).newInstance());
        } catch (Exception e) {
            throw new MockitoException("Internal problem occurred, please report it. " +
                    "Mockito is unable to load the default implementation of class that is a part of Mockito distribution. " +
                    "Failed to load " + pluginType, e);
        }
    }

    /**
     * Equivalent to {@link java.util.ServiceLoader#load} but without requiring
     * Java 6 / Android 2.3 (Gingerbread).
     */
    static <T> List<T> loadImplementations(Class<T> service) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        if (loader == null) {
            loader = ClassLoader.getSystemClassLoader();
        }
        Enumeration<URL> resources;
        try {
            resources = loader.getResources("mockito-extensions/" + service.getName());
        } catch (IOException e) {
            throw new MockitoException("Failed to load " + service, e);
        }

        List<T> result = new ArrayList<T>();
        for (URL resource : Collections.list(resources)) {
            InputStream in = null;
            try {
                in = resource.openStream();
                for (String line : readerToLines(new InputStreamReader(in, "UTF-8"))) {
                    String name = stripCommentAndWhitespace(line);
                    if (name.length() != 0) {
                        result.add(service.cast(loader.loadClass(name).newInstance()));
                    }
                }
            } catch (Exception e) {
                throw new MockitoConfigurationException(
                        "Failed to load " + service + " using " + resource, e);
            } finally {
                closeQuietly(in);
            }
        }
        return result;
    }

    static List<String> readerToLines(Reader reader) throws IOException {
        List<String> result = new ArrayList<String>();
        BufferedReader lineReader = new BufferedReader(reader);
        String line;
        while ((line = lineReader.readLine()) != null) {
            result.add(line);
        }
        return result;
    }

    static String stripCommentAndWhitespace(String line) {
        int hash = line.indexOf('#');
        if (hash != -1) {
            line = line.substring(0, hash);
        }
        return line.trim();
    }

    private static void closeQuietly(InputStream in) {
        if (in != null) {
            try {
                in.close();
            } catch (IOException ignored) {
            }
        }
    }
}
TOP

Related Classes of org.mockito.internal.configuration.ClassPathLoader

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.