3737import 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+ * */
4247public 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 );
0 commit comments