• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.internal.util.collections.Iterables;
8 import org.mockito.plugins.PluginSwitch;
9 
10 import java.io.IOException;
11 import java.lang.reflect.InvocationHandler;
12 import java.lang.reflect.Method;
13 import java.lang.reflect.Proxy;
14 import java.net.URL;
15 import java.util.Enumeration;
16 import java.util.HashMap;
17 import java.util.Map;
18 
19 class PluginLoader {
20 
21     private final PluginSwitch pluginSwitch;
22 
23     private final Map<String, String> alias;
24 
PluginLoader(PluginSwitch pluginSwitch)25     public PluginLoader(PluginSwitch pluginSwitch) {
26         this.pluginSwitch = pluginSwitch;
27         this.alias = new HashMap<String, String>();
28     }
29 
30     /**
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      */
withAlias(String name, String type)34     PluginLoader withAlias(String name, String type) {
35         alias.put(name, type);
36         return this;
37     }
38 
39     /**
40      * Scans the classpath for given pluginType. If not found, default class is used.
41      */
42     @SuppressWarnings("unchecked")
loadPlugin(final Class<T> pluginType, String defaultPluginClassName)43     <T> T loadPlugin(final Class<T> pluginType, String defaultPluginClassName) {
44         try {
45             T plugin = loadImpl(pluginType);
46             if (plugin != null) {
47                 return plugin;
48             }
49 
50             try {
51                 // Default implementation. Use our own ClassLoader instead of the context
52                 // ClassLoader, as the default implementation is assumed to be part of
53                 // Mockito and may not be available via the context ClassLoader.
54                 return pluginType.cast(Class.forName(defaultPluginClassName).newInstance());
55             } catch (Exception e) {
56                 throw new IllegalStateException("Internal problem occurred, please report it. " +
57                         "Mockito is unable to load the default implementation of class that is a part of Mockito distribution. " +
58                         "Failed to load " + pluginType, e);
59             }
60         } catch (final Throwable t) {
61             return (T) Proxy.newProxyInstance(pluginType.getClassLoader(),
62                     new Class<?>[]{pluginType},
63                     new InvocationHandler() {
64                         @Override
65                         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
66                             throw new IllegalStateException("Could not initialize plugin: " + pluginType, t);
67                         }
68                     });
69         }
70     }
71 
72     /**
73      * Equivalent to {@link java.util.ServiceLoader#load} but without requiring
74      * Java 6 / Android 2.3 (Gingerbread).
75      */
loadImpl(Class<T> service)76     private <T> T loadImpl(Class<T> service) {
77         ClassLoader loader = Thread.currentThread().getContextClassLoader();
78         if (loader == null) {
79             loader = ClassLoader.getSystemClassLoader();
80         }
81         Enumeration<URL> resources;
82         try {
83             resources = loader.getResources("mockito-extensions/" + service.getName());
84         } catch (IOException e) {
85             throw new IllegalStateException("Failed to load " + service, e);
86         }
87 
88         try {
89             String foundPluginClass = new PluginFinder(pluginSwitch).findPluginClass(Iterables.toIterable(resources));
90             if (foundPluginClass != null) {
91                 String aliasType = alias.get(foundPluginClass);
92                 if (aliasType != null) {
93                     foundPluginClass = aliasType;
94                 }
95                 Class<?> pluginClass = loader.loadClass(foundPluginClass);
96                 Object plugin = pluginClass.newInstance();
97                 return service.cast(plugin);
98             }
99             return null;
100         } catch (Exception e) {
101             throw new IllegalStateException(
102                     "Failed to load " + service + " implementation declared in " + resources, e);
103         }
104     }
105 }
106