Skip to content

Commit 419dbce

Browse files
m1a2strobobario
authored andcommitted
completed the refactor
Signed-off-by: Ken Huang <s7133700@gmail.com>
1 parent 149f27d commit 419dbce

1 file changed

Lines changed: 79 additions & 63 deletions

File tree

kroxylicious-runtime/src/main/java/io/kroxylicious/proxy/config/ServiceBasedPluginFactoryRegistry.java

Lines changed: 79 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
import java.util.Comparator;
1111
import java.util.HashMap;
1212
import java.util.HashSet;
13+
import java.util.List;
1314
import java.util.Map;
1415
import java.util.Objects;
16+
import java.util.Optional;
1517
import java.util.ServiceLoader;
1618
import java.util.Set;
1719
import java.util.concurrent.ConcurrentHashMap;
@@ -51,77 +53,91 @@ Map<String, ProviderAndConfigType> load(Class<?> pluginInterface) {
5153
}
5254

5355
private static Map<String, ProviderAndConfigType> loadProviders(Class<?> pluginInterface) {
54-
HashMap<String, Set<ProviderAndConfigType>> nameToProviders = new HashMap<>();
55-
ServiceLoader<?> load = ServiceLoader.load(pluginInterface);
56-
load.stream().forEach(provider -> {
57-
Class<?> providerType = provider.type();
58-
Plugin annotation = providerType.getAnnotation(Plugin.class);
59-
if (annotation == null) {
60-
LOGGER.atWarn()
61-
.addKeyValue("providerType", providerType)
62-
.addKeyValue("service", pluginInterface)
63-
.log("Failed to find @Plugin on provider of service");
64-
}
65-
else {
66-
ProviderAndConfigType providerAndConfigType = new ProviderAndConfigType(provider, annotation.configType());
67-
Stream<String> names = Stream.of(providerType.getName(), providerType.getSimpleName());
68-
names = maybeAddOldNames(providerType, names);
69-
names.forEach(name -> nameToProviders.compute(name, (k2, v) -> {
70-
if (v == null) {
71-
v = new HashSet<>();
72-
}
73-
v.add(providerAndConfigType);
74-
return v;
75-
}));
76-
}
77-
});
78-
var bySingleton = nameToProviders.entrySet().stream().collect(
56+
Map<String, Set<ProviderAndConfigType>> nameToProviders = new HashMap<>();
57+
ServiceLoader.load(pluginInterface).stream()
58+
.forEach(provider -> registerProvider(provider, nameToProviders, pluginInterface));
59+
var partitioned = nameToProviders.entrySet().stream().collect(
7960
Collectors.partitioningBy(e -> e.getValue().size() == 1));
61+
var ambiguousEntries = partitioned.get(false);
62+
var unambiguousEntries = partitioned.get(true);
8063
if (LOGGER.isWarnEnabled()) {
81-
for (Map.Entry<String, Set<ProviderAndConfigType>> ambiguousInstanceNameToProviders : bySingleton.get(false)) {
82-
String ambiguousKey = ambiguousInstanceNameToProviders.getKey();
83-
var implementationClasses = ambiguousInstanceNameToProviders.getValue().stream()
84-
.map(p -> p.provider().type())
85-
.sorted(Comparator.comparing(Class::getName))
86-
.toList();
87-
var fqCollision = implementationClasses.stream().filter(c -> c.isAnnotationPresent(DeprecatedPluginName.class))
88-
.flatMap(c -> implementationClasses.stream()
89-
.filter(c2 -> {
90-
var cOldName = c.getAnnotation(DeprecatedPluginName.class).oldName();
91-
return !c.equals(c2) && (c2.getName().equals(cOldName)
92-
|| (c2.isAnnotationPresent(DeprecatedPluginName.class) &&
93-
c2.getAnnotation(DeprecatedPluginName.class).oldName().equals(cOldName)));
94-
})
95-
.map(c2 -> Map.entry(c, c2)))
96-
.findFirst();
97-
if (fqCollision.isPresent()) {
98-
var entry = fqCollision.get();
99-
var annotatedClass = entry.getKey();
100-
var classWithCollidingFqName = entry.getValue();
101-
LOGGER.atWarn()
102-
.addKeyValue("annotatedClass", annotatedClass.getName())
103-
.addKeyValue("annotation", DeprecatedPluginName.class.getSimpleName())
104-
.addKeyValue("oldName", annotatedClass.getAnnotation(DeprecatedPluginName.class).oldName())
105-
.addKeyValue("collidingClass", classWithCollidingFqName.getName())
106-
.log("Plugin implementation class is annotated with @DeprecatedPluginName which collides with another plugin implementation class, you must remove one of these classes from the class path");
107-
throw new RuntimeException("Ambiguous plugin implementation name '" + ambiguousKey + "'");
108-
}
109-
else {
110-
LOGGER.atWarn()
111-
.addKeyValue("ambiguousKey", ambiguousKey)
112-
.addKeyValue("pluginInterface", pluginInterface.getSimpleName())
113-
.addKeyValue("candidates", implementationClasses.stream()
114-
.map(Class::getName)
115-
.collect(Collectors.joining(", ")))
116-
.log("Ambiguous reference to provider, it could refer to multiple implementations so to avoid ambiguous behaviour those fully qualified names must be used");
117-
}
64+
for (Map.Entry<String, Set<ProviderAndConfigType>> ambiguousEntry : ambiguousEntries) {
65+
handleAmbiguousEntry(ambiguousEntry, pluginInterface);
11866
}
11967
}
120-
return bySingleton.get(true).stream().collect(Collectors.toMap(
68+
return unambiguousEntries.stream().collect(Collectors.toMap(
12169
Map.Entry::getKey,
12270
e -> e.getValue().iterator().next()));
12371
}
12472

73+
private static void registerProvider(ServiceLoader.Provider<?> provider,
74+
Map<String, Set<ProviderAndConfigType>> nameToProviders,
75+
Class<?> pluginInterface) {
76+
Class<?> providerType = provider.type();
77+
Plugin annotation = providerType.getAnnotation(Plugin.class);
78+
if (annotation == null) {
79+
LOGGER.atWarn()
80+
.addKeyValue("providerType", providerType)
81+
.addKeyValue("service", pluginInterface)
82+
.log("Failed to find @Plugin on provider of service");
83+
return;
84+
}
85+
ProviderAndConfigType providerAndConfigType = new ProviderAndConfigType(provider, annotation.configType());
86+
Stream<String> names = Stream.of(providerType.getName(), providerType.getSimpleName());
87+
names = maybeAddOldNames(providerType, names);
88+
names.forEach(name -> nameToProviders.computeIfAbsent(name, k -> new HashSet<>()).add(providerAndConfigType));
89+
}
90+
91+
private static void handleAmbiguousEntry(Map.Entry<String, Set<ProviderAndConfigType>> ambiguousEntry,
92+
Class<?> pluginInterface) {
93+
String ambiguousKey = ambiguousEntry.getKey();
94+
List<Class<?>> implementationClasses = ambiguousEntry.getValue().stream()
95+
.<Class<?>> map(p -> p.provider().type())
96+
.sorted(Comparator.comparing(Class::getName))
97+
.toList();
98+
Optional<Map.Entry<Class<?>, Class<?>>> fqCollision = findDeprecatedNameCollision(implementationClasses);
99+
if (fqCollision.isPresent()) {
100+
var entry = fqCollision.get();
101+
var annotatedClass = entry.getKey();
102+
var classWithCollidingFqName = entry.getValue();
103+
LOGGER.atWarn()
104+
.addKeyValue("annotatedClass", annotatedClass.getName())
105+
.addKeyValue("annotation", DeprecatedPluginName.class.getSimpleName())
106+
.addKeyValue("oldName", annotatedClass.getAnnotation(DeprecatedPluginName.class).oldName())
107+
.addKeyValue("collidingClass", classWithCollidingFqName.getName())
108+
.log("Plugin implementation class is annotated with @DeprecatedPluginName which collides with another plugin implementation class, you must remove one of these classes from the class path");
109+
throw new RuntimeException("Ambiguous plugin implementation name '" + ambiguousKey + "'");
110+
}
111+
else {
112+
LOGGER.atWarn()
113+
.addKeyValue("ambiguousKey", ambiguousKey)
114+
.addKeyValue("pluginInterface", pluginInterface.getSimpleName())
115+
.addKeyValue("candidates", implementationClasses.stream()
116+
.map(Class::getName)
117+
.collect(Collectors.joining(", ")))
118+
.log("Ambiguous reference to provider, it could refer to multiple implementations so to avoid ambiguous behaviour those fully qualified names must be used");
119+
}
120+
}
121+
122+
private static Optional<Map.Entry<Class<?>, Class<?>>> findDeprecatedNameCollision(List<Class<?>> implementationClasses) {
123+
return implementationClasses.stream()
124+
.filter(c -> c.isAnnotationPresent(DeprecatedPluginName.class))
125+
.flatMap(c -> implementationClasses.stream()
126+
.filter(c2 -> isDeprecatedNameCollision(c, c2))
127+
.map(c2 -> Map.<Class<?>, Class<?>> entry(c, c2)))
128+
.findFirst();
129+
}
130+
131+
private static boolean isDeprecatedNameCollision(Class<?> annotatedClass, Class<?> other) {
132+
if (annotatedClass.equals(other)) {
133+
return false;
134+
}
135+
String oldName = annotatedClass.getAnnotation(DeprecatedPluginName.class).oldName();
136+
return other.getName().equals(oldName)
137+
|| (other.isAnnotationPresent(DeprecatedPluginName.class)
138+
&& other.getAnnotation(DeprecatedPluginName.class).oldName().equals(oldName));
139+
}
140+
125141
private static Stream<String> maybeAddOldNames(Class<?> providerType, Stream<String> names) {
126142
if (providerType.isAnnotationPresent(DeprecatedPluginName.class)) {
127143
String oldName = providerType.getAnnotation(DeprecatedPluginName.class).oldName();

0 commit comments

Comments
 (0)