Home | History | Annotate | Download | only in plugins
      1 /*
      2  * Copyright (c) 2016 Mockito contributors
      3  * This program is made available under the terms of the MIT License.
      4  */
      5 package org.mockito.internal.configuration.plugins;
      6 
      7 import org.mockito.plugins.PluginSwitch;
      8 
      9 import java.lang.reflect.InvocationHandler;
     10 import java.lang.reflect.Method;
     11 import java.lang.reflect.Proxy;
     12 
     13 class PluginLoader {
     14 
     15     private final DefaultMockitoPlugins plugins;
     16     private final PluginInitializer initializer;
     17 
     18     PluginLoader(DefaultMockitoPlugins plugins, PluginInitializer initializer) {
     19         this.plugins = plugins;
     20         this.initializer = initializer;
     21     }
     22 
     23     PluginLoader(PluginSwitch pluginSwitch) {
     24         this(new DefaultMockitoPlugins(), new PluginInitializer(pluginSwitch, null, new DefaultMockitoPlugins()));
     25     }
     26 
     27     /**
     28      * @deprecated Let's avoid adding more aliases. It complicates the API.
     29      * Instead of an alias, we can use fully qualified class name of the alternative implementation.
     30      * <p>
     31      * Adds an alias for a class name to this plugin loader. Instead of the fully qualified type name,
     32      * the alias can be used as a convenience name for a known plugin.
     33      */
     34     @Deprecated
     35     PluginLoader(PluginSwitch pluginSwitch, String alias) {
     36         this(new DefaultMockitoPlugins(), new PluginInitializer(pluginSwitch, alias, new DefaultMockitoPlugins()));
     37     }
     38 
     39     /**
     40      * Scans the classpath for given pluginType. If not found, default class is used.
     41      */
     42     @SuppressWarnings("unchecked")
     43     <T> T loadPlugin(final Class<T> pluginType) {
     44         return (T) loadPlugin(pluginType, null);
     45     }
     46 
     47     /**
     48      * Scans the classpath for given {@code preferredPluginType}. If not found scan for {@code
     49      * alternatePluginType}. If neither a preferred or alternate plugin is found, default to default
     50      * class of {@code preferredPluginType}.
     51      *
     52      * @return An object of either {@code preferredPluginType} or {@code alternatePluginType}
     53      */
     54     @SuppressWarnings("unchecked")
     55     <PreferredType, AlternateType> Object loadPlugin(final Class<PreferredType> preferredPluginType, final Class<AlternateType> alternatePluginType) {
     56         try {
     57             PreferredType preferredPlugin = initializer.loadImpl(preferredPluginType);
     58             if (preferredPlugin != null) {
     59                 return preferredPlugin;
     60             } else if (alternatePluginType != null) {
     61                 AlternateType alternatePlugin = initializer.loadImpl(alternatePluginType);
     62                 if (alternatePlugin != null) {
     63                     return alternatePlugin;
     64                 }
     65             }
     66 
     67             return plugins.getDefaultPlugin(preferredPluginType);
     68         } catch (final Throwable t) {
     69             return Proxy.newProxyInstance(preferredPluginType.getClassLoader(),
     70                 new Class<?>[]{preferredPluginType},
     71                 new InvocationHandler() {
     72                     @Override
     73                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     74                         throw new IllegalStateException("Could not initialize plugin: " + preferredPluginType + " (alternate: " + alternatePluginType + ")", t);
     75                     }
     76                 });
     77         }
     78     }
     79 }
     80