org.elasticsearch.plugins.PluginIntrospector Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of elasticsearch Show documentation
Show all versions of elasticsearch Show documentation
Elasticsearch subproject :server
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
package org.elasticsearch.plugins;
import org.elasticsearch.core.SuppressForbidden;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.util.stream.Collectors.toMap;
final class PluginIntrospector {
private final Set> pluginClasses = Set.of(
Plugin.class,
ActionPlugin.class,
AnalysisPlugin.class,
CircuitBreakerPlugin.class,
ClusterPlugin.class,
DiscoveryPlugin.class,
EnginePlugin.class,
ExtensiblePlugin.class,
HealthPlugin.class,
IndexStorePlugin.class,
IngestPlugin.class,
MapperPlugin.class,
NetworkPlugin.class,
PersistentTaskPlugin.class,
RecoveryPlannerPlugin.class,
ReloadablePlugin.class,
RepositoryPlugin.class,
ScriptPlugin.class,
SearchPlugin.class,
ShutdownAwarePlugin.class,
SystemIndexPlugin.class
);
private final Set> deprecatedPluginClasses = pluginClasses.stream()
.filter(c -> c.isAnnotationPresent(Deprecated.class))
.collect(Collectors.toUnmodifiableSet());
private record MethodType(String name, Class>[] parameterTypes, boolean isDeprecated) {}
private final Map, List> pluginMethodsMap;
private final Map, List> pluginDeprecatedMethodsMap;
private PluginIntrospector() {
pluginMethodsMap = pluginClasses.stream().collect(toMap(Function.identity(), PluginIntrospector::findMethods));
pluginDeprecatedMethodsMap = pluginMethodsMap.entrySet()
.stream()
.map(e -> Map.entry(e.getKey(), e.getValue().stream().filter(MethodType::isDeprecated).toList()))
.filter(e -> e.getValue().isEmpty() == false)
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
}
static PluginIntrospector getInstance() {
return new PluginIntrospector();
}
/**
* Returns the list of Elasticsearch plugin interfaces implemented by the given plugin
* implementation class. The list contains the simple names of the interfaces.
*/
List interfaces(final Class> pluginClass) {
assert Plugin.class.isAssignableFrom(pluginClass);
return interfaceClasses(pluginClass, pluginClasses::contains).map(Class::getSimpleName).sorted().toList();
}
/**
* Returns the list of methods overridden by the given plugin implementation class. The list
* contains the simple names of the methods.
*/
List overriddenMethods(final Class> pluginClass) {
return findOverriddenMethods(pluginClass, pluginMethodsMap).keySet().stream().sorted().toList();
}
/**
* Returns the list of deprecated Elasticsearch plugin interfaces which are implemented by the
* given plugin implementation class. The list contains the simple names of the interfaces.
*/
List deprecatedInterfaces(final Class> pluginClass) {
assert Plugin.class.isAssignableFrom(pluginClass);
return interfaceClasses(pluginClass, deprecatedPluginClasses::contains).map(Class::getSimpleName).sorted().toList();
}
/**
* Returns the deprecated methods from Elasticsearch plugin interfaces which are implemented by
* the given plugin implementation class. The map is from the simple method name to the simple interface class name.
*
* @apiNote The simple names work as a key because they are unique across all plugin interfaces.
*/
Map deprecatedMethods(final Class> pluginClass) {
return findOverriddenMethods(pluginClass, pluginDeprecatedMethodsMap);
}
// finds the subset of given methods that are overridden by the given class
// returns a map of method name to interface name the method was declared in
private static Map findOverriddenMethods(final Class> pluginClass, Map, List> methodsMap) {
assert Plugin.class.isAssignableFrom(pluginClass);
List> clazzes = Stream.concat(Stream.of(Plugin.class), interfaceClasses(pluginClass, methodsMap::containsKey)).toList();
if (clazzes.isEmpty()) {
return Map.of();
}
Map overriddenMethods = new HashMap<>();
for (var clazz : clazzes) {
List methods = methodsMap.get(clazz);
if (methods == null) {
continue;
}
for (var mt : methods) {
try {
Method m = pluginClass.getMethod(mt.name(), mt.parameterTypes());
if (m.getDeclaringClass() == clazz) {
// it's not overridden
} else {
assert clazz.isAssignableFrom(m.getDeclaringClass());
var existing = overriddenMethods.put(mt.name(), clazz.getSimpleName());
assert existing == null;
}
} catch (NoSuchMethodException unexpected) {
throw new AssertionError(unexpected);
}
}
}
return Map.copyOf(overriddenMethods);
}
// Returns the non-static methods declared in the given class.
@SuppressForbidden(reason = "Need declared methods")
private static List findMethods(Class> cls) {
assert cls.getName().startsWith("org.elasticsearch.plugins");
assert cls.isInterface() || cls == Plugin.class : cls;
return Arrays.stream(cls.getDeclaredMethods())
.filter(m -> Modifier.isStatic(m.getModifiers()) == false)
.map(m -> new MethodType(m.getName(), m.getParameterTypes(), m.isAnnotationPresent(Deprecated.class)))
.toList();
}
// Returns a stream of o.e.XXXPlugin interfaces, that the given plugin class implements.
private static Stream> interfaceClasses(Class> pluginClass, Predicate> classPredicate) {
assert Plugin.class.isAssignableFrom(pluginClass);
Set> pluginInterfaces = new HashSet<>();
do {
Arrays.stream(pluginClass.getInterfaces()).forEach(inf -> superInterfaces(inf, pluginInterfaces, classPredicate));
} while ((pluginClass = pluginClass.getSuperclass()) != java.lang.Object.class);
return pluginInterfaces.stream();
}
private static void superInterfaces(Class> c, Set> interfaces, Predicate> classPredicate) {
if (classPredicate.test(c)) {
interfaces.add(c);
}
Arrays.stream(c.getInterfaces()).forEach(inf -> superInterfaces(inf, interfaces, classPredicate));
}
}