jdaugherty commented on code in PR #15409: URL: https://github.com/apache/grails-core/pull/15409#discussion_r2927448735
########## grails-core/src/main/groovy/org/apache/grails/core/plugins/GrailsPluginDiscovery.java: ########## @@ -0,0 +1,593 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.grails.core.plugins; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +import groovy.lang.GroovyClassLoader; +import org.codehaus.groovy.control.CompilationFailedException; +import org.codehaus.groovy.runtime.IOGroovyMethods; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.springframework.core.env.Environment; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; + +import grails.plugins.GrailsPluginSorter; +import grails.plugins.GrailsVersionUtils; +import grails.plugins.PluginFilter; +import grails.plugins.exceptions.PluginException; +import grails.util.Metadata; +import org.apache.grails.core.plugins.filters.PluginFilterRetriever; +import org.grails.core.io.CachingPathMatchingResourcePatternResolver; +import org.grails.io.support.GrailsResourceUtils; + +/** + * This class provides the canonical implementations of Grails Plugin Discovery. + * + * @since 7.1 + */ +public class GrailsPluginDiscovery { + + public static final String BEAN_NAME = "grailsPluginDiscovery"; + private static final Logger LOG = LoggerFactory.getLogger(GrailsPluginDiscovery.class); + + protected Metadata applicationMeta = Metadata.getCurrent(); + protected Resource[] pluginResources = new Resource[0]; + protected Class<?>[] pluginClasses = new Class[0]; + /** + * plugins ordered by the load order + */ + protected LinkedHashMap<String, GrailsPluginInfo> plugins; + /** + * plugins sorted by the topographical sort + */ + protected List<GrailsPluginInfo> orderedPlugins; + /** + * plugins sorted by the load order + */ + protected List<GrailsPluginInfo> loadOrderedPlugins; + protected List<GrailsPluginInfo> dynamicPlugins; + protected Map<String, Set<GrailsPluginInfo>> pluginToObserverMap; + protected List<GrailsPluginInfo> delayedLoadPlugins; + protected Map<String, GrailsPluginInfo> failedPlugins; + protected Map<GrailsPluginInfo, String[]> delayedEvictions; + protected PluginFilter pluginFilter; + protected boolean loadClasspathPlugins = true; + protected boolean requireClasspathPlugin = true; + protected final PluginFilterRetriever filterRetriever; + + public GrailsPluginDiscovery() { + this(new PluginFilterRetriever()); + } + + public GrailsPluginDiscovery(String resourcePath) { + this(); + + PathMatchingResourcePatternResolver resolver = CachingPathMatchingResourcePatternResolver.INSTANCE; + try { + pluginResources = resolver.getResources(resourcePath); + } catch (IOException ioe) { + LOG.debug("Unable to load plugins for resource path {}", resourcePath, ioe); + } + } + + public GrailsPluginDiscovery(Class<?>[] pluginClasses) { + this(); + this.pluginClasses = pluginClasses; + } + + public GrailsPluginDiscovery(String[] pluginResources) { + this(); + + PathMatchingResourcePatternResolver resolver = CachingPathMatchingResourcePatternResolver.INSTANCE; + + List<Resource> resourceList = new ArrayList<>(); + for (String resourcePath : pluginResources) { + try { + resourceList.addAll(Arrays.asList(resolver.getResources(resourcePath))); + } catch (IOException ioe) { + LOG.debug("Unable to load plugins for resource path {}", resourcePath, ioe); + } + } + + this.pluginResources = resourceList.toArray(new Resource[0]); + } + + public GrailsPluginDiscovery(Resource[] pluginFiles) { + this(); + this.pluginResources = pluginFiles; + } + + public GrailsPluginDiscovery(PluginFilterRetriever filterRetriever) { + this.filterRetriever = filterRetriever; + } + + public GrailsPluginInfo[] getDynamicPlugins() { + return dynamicPlugins.toArray(new GrailsPluginInfo[0]); + } + + public Map<String, GrailsPluginInfo> getFailedPlugins() { + return failedPlugins; + } + + public GrailsPluginInfo getPlugin(String pluginName, Object version, Environment environment) { + if (plugins == null) { + if (environment == null) { + throw new IllegalArgumentException("Environment must be provided to fetch a plugin"); + } + findPlugins(environment); + } + + GrailsPluginInfo plugin = plugins.get(GrailsPluginUtils.normalizePluginName(pluginName)); + if (plugin != null && GrailsVersionUtils.isValidVersion(plugin.pluginVersion(), version.toString())) { + return plugin; + } + return null; + } + + public boolean hasPlugin(String name) { + return plugins.containsKey(GrailsPluginUtils.normalizePluginName(name)); + } + + public GrailsPluginInfo getPlugin(String pluginName, Environment environment) { + if (plugins == null) { + if (environment == null) { + throw new IllegalArgumentException("Environment must be provided to fetch a plugin"); + } + + findPlugins(environment); + } + + return plugins.get(GrailsPluginUtils.normalizePluginName(pluginName)); + } + + public Collection<GrailsPluginInfo> getPluginObservers(GrailsPluginInfo plugin) { + Objects.requireNonNull(plugin, "Argument [plugin] cannot be null"); + + Collection<GrailsPluginInfo> c = pluginToObserverMap.get(plugin.name()); + + // Add any wildcard observers. + Collection<GrailsPluginInfo> wildcardObservers = pluginToObserverMap.get("*"); + if (wildcardObservers != null) { + if (c != null) { + c.addAll(wildcardObservers); + } else { + c = wildcardObservers; + } + } + + if (c != null) { + // Make sure this plugin is not observing itself! + c.remove(plugin); + return c; + } + + return Collections.emptySet(); + } + + public Collection<GrailsPluginInfo> getPlugins(Environment environment) { + return plugins == null ? findPlugins(environment).values() : plugins.values(); + } + + /** + * @return plugins ordered by a topographical sort. + */ + public Collection<GrailsPluginInfo> getOrderedPlugins(Environment environment) { + if (orderedPlugins == null) { + findPlugins(environment); + } + + return orderedPlugins; + } + + /** + * @return the order the plugins were loaded in. + */ + public Collection<GrailsPluginInfo> getLoadOrderedPlugins(Environment environment) { Review Comment: I'm going to refactor the GrailsPluginDiscovery class so that it has a common interface & then we ship a default implementation. I'm also going to add an init() method instead of initing on the first time this is called. This way the getters can be true getters. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
