1 /*
2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package jdk.tools.jlink.internal;
26 
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Objects;
33 import java.util.ServiceLoader;
34 import jdk.tools.jlink.plugin.Plugin;
35 import jdk.tools.jlink.plugin.PluginException;
36 
37 /**
38  *
39  * Plugin Providers repository. Plugin Providers are
40  * retrieved thanks to the ServiceLoader mechanism.
41  */
42 public final class PluginRepository {
43 
PluginRepository()44     private PluginRepository() {
45     }
46 
47     private static final Map<String, Plugin> registeredPlugins = new HashMap<>();
48 
49     /**
50      * Retrieves the plugin associated to the passed name. If multiple providers
51      * exist for the same name,
52      * then an exception is thrown.
53      * @param name The plugin provider name.
54      * @param pluginsLayer
55      * @return A provider or null if not found.
56      */
getPlugin(String name, ModuleLayer pluginsLayer)57     public static Plugin getPlugin(String name,
58             ModuleLayer pluginsLayer) {
59         return getPlugin(Plugin.class, name, pluginsLayer);
60     }
61 
62     /**
63      * Build plugin for the passed name.
64      *
65      * @param config Optional config.
66      * @param name Non null name.
67      * @param pluginsLayer
68      * @return A plugin or null if no plugin found.
69      */
newPlugin(Map<String, String> config, String name, ModuleLayer pluginsLayer)70     public static Plugin newPlugin(Map<String, String> config, String name,
71             ModuleLayer pluginsLayer) {
72         Objects.requireNonNull(name);
73         Objects.requireNonNull(pluginsLayer);
74         Plugin plugin = getPlugin(name, pluginsLayer);
75         if (plugin != null) {
76             try {
77                 plugin.configure(config);
78             } catch (IllegalArgumentException e) {
79                 if (JlinkTask.DEBUG) {
80                     System.err.println("Plugin " + plugin.getName() + " threw exception with config: " + config);
81                     e.printStackTrace();
82                 }
83                 throw e;
84             }
85         }
86         return plugin;
87     }
88 
89     /**
90      * Explicit registration of a plugin in the repository. Used by unit tests
91      * @param plugin The plugin to register.
92      */
registerPlugin(Plugin plugin)93     public synchronized static void registerPlugin(Plugin plugin) {
94         Objects.requireNonNull(plugin);
95         registeredPlugins.put(plugin.getName(), plugin);
96     }
97 
98     /**
99      * Explicit unregistration of a plugin in the repository. Used by unit
100      * tests
101      *
102      * @param name Plugin name
103      */
unregisterPlugin(String name)104     public synchronized static void unregisterPlugin(String name) {
105         Objects.requireNonNull(name);
106         registeredPlugins.remove(name);
107     }
108 
getPlugins(ModuleLayer pluginsLayer)109     public static List<Plugin> getPlugins(ModuleLayer pluginsLayer) {
110         return getPlugins(Plugin.class, pluginsLayer);
111     }
112 
getPlugin(Class<T> clazz, String name, ModuleLayer pluginsLayer)113     private static <T extends Plugin> T getPlugin(Class<T> clazz, String name,
114             ModuleLayer pluginsLayer) {
115         Objects.requireNonNull(name);
116         Objects.requireNonNull(pluginsLayer);
117         @SuppressWarnings("unchecked")
118         T provider = null;
119         List<T> javaProviders = getPlugins(clazz, pluginsLayer);
120         for(T factory : javaProviders) {
121             if (factory.getName().equals(name)) {
122                 if (provider != null) {
123                     throw new PluginException("Multiple plugin "
124                             + "for the name " + name);
125                 }
126                 provider = factory;
127             }
128         }
129         return provider;
130     }
131 
132     /**
133      * The plugins accessible in the current context.
134      *
135      * @param pluginsLayer
136      * @return The list of plugins.
137      */
getPlugins(Class<T> clazz, ModuleLayer pluginsLayer)138     private static <T extends Plugin> List<T> getPlugins(Class<T> clazz, ModuleLayer pluginsLayer) {
139         Objects.requireNonNull(pluginsLayer);
140         List<T> factories = new ArrayList<>();
141         try {
142             Iterator<T> providers
143                     = ServiceLoader.load(pluginsLayer, clazz).iterator();
144             while (providers.hasNext()) {
145                 factories.add(providers.next());
146             }
147             registeredPlugins.values().stream().forEach((fact) -> {
148                 if (clazz.isInstance(fact)) {
149                     @SuppressWarnings("unchecked")
150                     T trans = (T) fact;
151                     factories.add(trans);
152                 }
153             });
154             return factories;
155         } catch (Exception ex) {
156             throw new PluginException(ex);
157         }
158     }
159 
160 }
161