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 */
25package jdk.tools.jlink.internal;
26
27import java.util.ArrayList;
28import java.util.HashMap;
29import java.util.Iterator;
30import java.util.List;
31import java.util.Map;
32import java.util.Objects;
33import java.util.ServiceLoader;
34import jdk.tools.jlink.plugin.Plugin;
35import jdk.tools.jlink.plugin.PluginException;
36
37/**
38 *
39 * Plugin Providers repository. Plugin Providers are
40 * retrieved thanks to the ServiceLoader mechanism.
41 */
42public final class PluginRepository {
43
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     */
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     */
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     */
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     */
104    public synchronized static void unregisterPlugin(String name) {
105        Objects.requireNonNull(name);
106        registeredPlugins.remove(name);
107    }
108
109    public static List<Plugin> getPlugins(ModuleLayer pluginsLayer) {
110        return getPlugins(Plugin.class, pluginsLayer);
111    }
112
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     */
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