Skip to content

Commit bb0074f

Browse files
authored
Removed static part from PluginRegistry to avoid static initializer (#12799)
Removed static part from PluginRegistry to avoid static initializer, transforming it into a singleton. Reflections library is not thread safe in jar archives scan, so there must be only one instance of PluginRegistry
1 parent 6f55066 commit bb0074f

File tree

3 files changed

+56
-33
lines changed

3 files changed

+56
-33
lines changed

logstash-core/src/main/java/org/logstash/plugins/PluginLookup.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.jruby.runtime.builtin.IRubyObject;
3333
import org.logstash.RubyUtil;
3434
import org.logstash.plugins.discovery.PluginRegistry;
35+
import org.logstash.plugins.factory.PluginFactoryExt;
3536

3637
import java.util.stream.Collectors;
3738
import java.util.stream.Stream;
@@ -40,20 +41,23 @@
4041
* Java Implementation of the plugin that is implemented by wrapping the Ruby
4142
* {@code LogStash::Plugin} class for the Ruby plugin lookup.
4243
*/
43-
public final class PluginLookup {
44+
public final class PluginLookup implements PluginFactoryExt.PluginResolver {
4445

4546
private static final IRubyObject RUBY_REGISTRY = RubyUtil.RUBY.executeScript(
4647
"require 'logstash/plugins/registry'\nrequire 'logstash/plugin'\nLogStash::Plugin",
4748
""
4849
);
4950

50-
private PluginLookup() {
51-
// Utility Class
51+
private final PluginRegistry pluginRegistry;
52+
53+
public PluginLookup(PluginRegistry pluginRegistry) {
54+
this.pluginRegistry = pluginRegistry;
5255
}
5356

5457
@SuppressWarnings("rawtypes")
55-
public static PluginLookup.PluginClass lookup(final PluginLookup.PluginType type, final String name) {
56-
Class<?> javaClass = PluginRegistry.getPluginClass(type, name);
58+
@Override
59+
public PluginClass resolve(PluginType type, String name) {
60+
Class<?> javaClass = pluginRegistry.getPluginClass(type, name);
5761
if (javaClass != null) {
5862

5963
if (!PluginValidator.validatePlugin(type, javaClass)) {

logstash-core/src/main/java/org/logstash/plugins/discovery/PluginRegistry.java

Lines changed: 45 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,40 +37,58 @@
3737
import java.util.Set;
3838

3939
/**
40-
* Registry for built-in Java plugins (not installed via logstash-plugin)
41-
*/
40+
* Registry for built-in Java plugins (not installed via logstash-plugin).
41+
* This is singleton ofr two reasons:
42+
* <ul>
43+
* <li>it's a registry so no need for multiple instances</li>
44+
* <li>the Reflections library used need to run in single thread during discovery phase</li>
45+
* </ul>
46+
* */
4247
public final class PluginRegistry {
4348

44-
private static final Map<String, Class<Input>> INPUTS = new HashMap<>();
45-
private static final Map<String, Class<Filter>> FILTERS = new HashMap<>();
46-
private static final Map<String, Class<Output>> OUTPUTS = new HashMap<>();
47-
private static final Map<String, Class<Codec>> CODECS = new HashMap<>();
49+
private final Map<String, Class<Input>> inputs = new HashMap<>();
50+
private final Map<String, Class<Filter>> filters = new HashMap<>();
51+
private final Map<String, Class<Output>> outputs = new HashMap<>();
52+
private final Map<String, Class<Codec>> codecs = new HashMap<>();
53+
private static final Object LOCK = new Object();
54+
private static volatile PluginRegistry INSTANCE;
4855

49-
static {
56+
private PluginRegistry() {
5057
discoverPlugins();
5158
}
5259

53-
private PluginRegistry() {} // utility class
54-
60+
public static PluginRegistry getInstance() {
61+
if (INSTANCE == null) {
62+
synchronized (LOCK) {
63+
if (INSTANCE == null) {
64+
INSTANCE = new PluginRegistry();
65+
}
66+
}
67+
}
68+
return INSTANCE;
69+
}
70+
5571
@SuppressWarnings("unchecked")
56-
private static void discoverPlugins() {
72+
private void discoverPlugins() {
73+
// the constructor of Reflection must be called only by one thread, else there is a
74+
// risk that the first thread that completes close the Zip files for the others.
5775
Reflections reflections = new Reflections("org.logstash.plugins");
5876
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(LogstashPlugin.class);
5977
for (final Class<?> cls : annotated) {
6078
for (final Annotation annotation : cls.getAnnotations()) {
6179
if (annotation instanceof LogstashPlugin) {
6280
String name = ((LogstashPlugin) annotation).name();
6381
if (Filter.class.isAssignableFrom(cls)) {
64-
FILTERS.put(name, (Class<Filter>) cls);
82+
filters.put(name, (Class<Filter>) cls);
6583
}
6684
if (Output.class.isAssignableFrom(cls)) {
67-
OUTPUTS.put(name, (Class<Output>) cls);
85+
outputs.put(name, (Class<Output>) cls);
6886
}
6987
if (Input.class.isAssignableFrom(cls)) {
70-
INPUTS.put(name, (Class<Input>) cls);
88+
inputs.put(name, (Class<Input>) cls);
7189
}
7290
if (Codec.class.isAssignableFrom(cls)) {
73-
CODECS.put(name, (Class<Codec>) cls);
91+
codecs.put(name, (Class<Codec>) cls);
7492
}
7593

7694
break;
@@ -79,7 +97,7 @@ private static void discoverPlugins() {
7997
}
8098
}
8199

82-
public static Class<?> getPluginClass(PluginLookup.PluginType pluginType, String pluginName) {
100+
public Class<?> getPluginClass(PluginLookup.PluginType pluginType, String pluginName) {
83101
if (pluginType == PluginLookup.PluginType.FILTER) {
84102
return getFilterClass(pluginName);
85103
}
@@ -97,31 +115,31 @@ public static Class<?> getPluginClass(PluginLookup.PluginType pluginType, String
97115

98116
}
99117

100-
public static Class<Input> getInputClass(String name) {
101-
return INPUTS.get(name);
118+
public Class<Input> getInputClass(String name) {
119+
return inputs.get(name);
102120
}
103121

104-
public static Class<Filter> getFilterClass(String name) {
105-
return FILTERS.get(name);
122+
public Class<Filter> getFilterClass(String name) {
123+
return filters.get(name);
106124
}
107125

108-
public static Class<Codec> getCodecClass(String name) {
109-
return CODECS.get(name);
126+
public Class<Codec> getCodecClass(String name) {
127+
return codecs.get(name);
110128
}
111129

112-
public static Class<Output> getOutputClass(String name) {
113-
return OUTPUTS.get(name);
130+
public Class<Output> getOutputClass(String name) {
131+
return outputs.get(name);
114132
}
115133

116-
public static Codec getCodec(String name, Configuration configuration, Context context) {
117-
if (name != null && CODECS.containsKey(name)) {
118-
return instantiateCodec(CODECS.get(name), configuration, context);
134+
public Codec getCodec(String name, Configuration configuration, Context context) {
135+
if (name != null && codecs.containsKey(name)) {
136+
return instantiateCodec(codecs.get(name), configuration, context);
119137
}
120138
return null;
121139
}
122140

123141
@SuppressWarnings({"unchecked","rawtypes"})
124-
private static Codec instantiateCodec(Class clazz, Configuration configuration, Context context) {
142+
private Codec instantiateCodec(Class clazz, Configuration configuration, Context context) {
125143
try {
126144
Constructor<Codec> constructor = clazz.getConstructor(Configuration.class, Context.class);
127145
return constructor.newInstance(configuration, context);

logstash-core/src/main/java/org/logstash/plugins/factory/PluginFactoryExt.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.logstash.instrument.metrics.MetricKeys;
2121
import org.logstash.plugins.ConfigVariableExpander;
2222
import org.logstash.plugins.PluginLookup;
23+
import org.logstash.plugins.discovery.PluginRegistry;
2324

2425
import java.util.*;
2526
import java.util.concurrent.ConcurrentHashMap;
@@ -81,7 +82,7 @@ public static IRubyObject filterDelegator(final ThreadContext context,
8182
}
8283

8384
public PluginFactoryExt(final Ruby runtime, final RubyClass metaClass) {
84-
this(runtime, metaClass, PluginLookup::lookup);
85+
this(runtime, metaClass, new PluginLookup(PluginRegistry.getInstance()));
8586
}
8687

8788
PluginFactoryExt(final Ruby runtime, final RubyClass metaClass, PluginResolver pluginResolver) {

0 commit comments

Comments
 (0)