44import java .util .ArrayList ;
55import java .util .Collection ;
66import java .util .HashMap ;
7- import java .util .HashSet ;
87import java .util .Iterator ;
8+ import java .util .LinkedHashSet ;
99import java .util .List ;
1010import java .util .Map ;
1111import java .util .Optional ;
1212import java .util .ServiceConfigurationError ;
1313import java .util .ServiceLoader ;
14+ import java .util .Set ;
1415import java .util .regex .Pattern ;
1516
1617import org .apache .commons .lang3 .StringUtils ;
@@ -34,12 +35,21 @@ public class PluginsOfType<T extends Plugin> {
3435 */
3536 private static final Pattern PLUGIN_ID_PATTERN = Pattern .compile ("[\\ p{L}\\ p{N}\\ -._]+" );
3637
38+ /** Plugin type we're managing. */
3739 private final Class <T > pluginClass ;
3840
39- /**
40- * Plugins by their id
41+ /** Registered plugins of this type. */
42+ private final Set <PluginData <T >> plugins = new LinkedHashSet <>();
43+
44+ /** Mapping from ids to plugins.
45+ *
46+ * Note that multiple ids may point to the same plugin,
47+ * e.g. [fully qualified] class name, script name, value returned by plugin.getId().
48+ * Usually you will use the script name for Groovy plugins and the class name for Java plugins.
49+ * plugin.getId() should only be used if you want to support a different id as well, e.g.
50+ * in the case of QueryFunctionAbs we want to refer to it as just "abs".
4151 */
42- private final Map <String , PluginData <T >> pluginsById = new HashMap <>();
52+ private final Map <String , PluginData <T >> idToPlugin = new HashMap <>();
4353
4454 PluginsOfType (Class <T > pluginClass , BLConfigPlugins pluginConfig , URLClassLoader cl ) {
4555 this .pluginClass = pluginClass ;
@@ -96,8 +106,13 @@ void register(Plugin plugin, BLConfigPlugins configs, String alternateId) {
96106
97107 private void add (String id , PluginData <T > data ) {
98108 if (BlackLab .isPluginAllowed (data .getPlugin ())) {
99- synchronized (pluginsById ) {
100- pluginsById .putIfAbsent (id .toLowerCase (), data );
109+ synchronized (this ) {
110+ if (!idToPlugin .containsKey (id )) {
111+ idToPlugin .put (id .toLowerCase (), data );
112+ plugins .add (data );
113+ } else if (!idToPlugin .get (id ).equals (data )){
114+ logger .warn ("Plugin id collision, plugin '" + id + "' was already registered." );
115+ }
101116 }
102117 } else
103118 logger .warn ("Skipping plugin '" + id + "'; it's not on the plugins.allowed list)" );
@@ -107,16 +122,15 @@ public Collection<T> getAll() {
107122 PluginManager .loadAllGroovyScripts ();
108123 List <T > result = new ArrayList <>();
109124 Collection <PluginData <T >> pluginDatas ;
110- synchronized (pluginsById ) {
111- pluginDatas = pluginsById . values ( );
125+ synchronized (this ) {
126+ pluginDatas = new ArrayList <>( plugins );
112127 }
113128 for (PluginData <T > data : pluginDatas ) {
114129 T plugin = data .getPlugin ();
115130 if (pluginClass .isInstance (plugin )) {
116131 try {
117132 data .initializePlugin ();
118- if (!result .contains (plugin ))
119- result .add (pluginClass .cast (plugin ));
133+ result .add (pluginClass .cast (plugin ));
120134 } catch (PluginException e ) {
121135 // exception already cached in plugindata, no need to throw.
122136 logger .error ("Plugin {} failed to initialize: {}" , plugin .getId (), e .getMessage ());
@@ -150,14 +164,14 @@ public boolean exists(String id) {
150164
151165 public Optional <T > getIfExists (String id ) throws PluginException {
152166 PluginData <T > pluginData ;
153- synchronized (pluginsById ) {
154- pluginData = pluginsById .get (id .toLowerCase ());
167+ synchronized (this ) {
168+ pluginData = idToPlugin .get (id .toLowerCase ());
155169 }
156170 if (pluginData == null ) {
157171 // Maybe this is a groovy script we haven't loaded yet.
158172 PluginManager .getUnloaded (id );
159- synchronized (pluginsById ) {
160- pluginData = pluginsById .get (id .toLowerCase ());
173+ synchronized (this ) {
174+ pluginData = idToPlugin .get (id .toLowerCase ());
161175 }
162176 }
163177 if (pluginData == null ) {
@@ -174,8 +188,8 @@ public Optional<T> getIfExists(String id) throws PluginException {
174188 */
175189 void initializePlugins () {
176190 Collection <PluginData <T >> pluginDatas ;
177- synchronized (pluginsById ) {
178- pluginDatas = new HashSet <>(pluginsById . values () );
191+ synchronized (this ) {
192+ pluginDatas = new ArrayList <>(plugins );
179193 }
180194 pluginDatas .forEach (pluginData -> {
181195 try {
0 commit comments