murblanc commented on a change in pull request #1962:
URL: https://github.com/apache/lucene-solr/pull/1962#discussion_r517211398



##########
File path: 
solr/core/src/java/org/apache/solr/cluster/events/impl/ClusterEventProducerFactory.java
##########
@@ -0,0 +1,173 @@
+package org.apache.solr.cluster.events.impl;
+
+import org.apache.solr.api.ContainerPluginsRegistry;
+import org.apache.solr.cluster.events.ClusterEvent;
+import org.apache.solr.cluster.events.ClusterEventListener;
+import org.apache.solr.cluster.events.ClusterEventProducer;
+import org.apache.solr.cluster.events.ClusterEventProducerBase;
+import org.apache.solr.cluster.events.NoOpProducer;
+import org.apache.solr.core.CoreContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.invoke.MethodHandles;
+import java.util.Set;
+
+/**
+ * This class helps in handling the initial registration of plugin-based 
listeners,
+ * when both the final {@link ClusterEventProducer} implementation and 
listeners
+ * are configured using plugins.
+ */
+public class ClusterEventProducerFactory extends ClusterEventProducerBase {
+  private static final Logger log = 
LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  private ContainerPluginsRegistry.PluginRegistryListener 
initialPluginListener;
+  private boolean created = false;
+
+  public ClusterEventProducerFactory(CoreContainer cc) {
+    super(cc);
+    initialPluginListener = new 
ContainerPluginsRegistry.PluginRegistryListener() {
+      @Override
+      public void added(ContainerPluginsRegistry.ApiInfo plugin) {
+        if (plugin == null || plugin.getInstance() == null) {
+          return;
+        }
+        Object instance = plugin.getInstance();
+        if (instance instanceof ClusterEventListener) {
+          registerListener((ClusterEventListener) instance);
+        }
+      }
+
+      @Override
+      public void deleted(ContainerPluginsRegistry.ApiInfo plugin) {
+        if (plugin == null || plugin.getInstance() == null) {
+          return;
+        }
+        Object instance = plugin.getInstance();
+        if (instance instanceof ClusterEventListener) {
+          unregisterListener((ClusterEventListener) instance);
+        }
+      }
+
+      @Override
+      public void modified(ContainerPluginsRegistry.ApiInfo old, 
ContainerPluginsRegistry.ApiInfo replacement) {
+        added(replacement);
+        deleted(old);
+      }
+    };
+  }
+
+  @Override
+  public Set<ClusterEvent.EventType> getSupportedEventTypes() {
+    return NoOpProducer.ALL_EVENT_TYPES;
+  }
+
+  /**
+   * This method returns an initial plugin registry listener that helps to 
capture the
+   * freshly loaded listener plugins before the final cluster event producer 
is created.
+   * @return initial listener
+   */
+  public ContainerPluginsRegistry.PluginRegistryListener 
getPluginRegistryListener() {
+    return initialPluginListener;
+  }
+
+  /**
+   * Create a {@link ClusterEventProducer} based on the current plugin 
configurations.
+   * <p>NOTE: this method can only be called once because it has side-effects, 
such as
+   * transferring the initially collected listeners to the resulting 
producer's instance, and
+   * installing a {@link 
org.apache.solr.api.ContainerPluginsRegistry.PluginRegistryListener}.
+   * Calling this method more than once will result in an exception.</p>
+   * @param plugins current plugin configurations
+   * @return configured instance of cluster event producer (with side-effects, 
see above)
+   */
+  public DelegatingClusterEventProducer create(ContainerPluginsRegistry 
plugins) {
+    if (created) {
+      throw new RuntimeException("this factory can be called only once!");
+    }
+    final DelegatingClusterEventProducer clusterEventProducer = new 
DelegatingClusterEventProducer(cc);
+    // since this is a ClusterSingleton, register it as such
+    
cc.getClusterSingletons().getSingletons().put(ClusterEventProducer.PLUGIN_NAME 
+"_delegate", clusterEventProducer);
+    ContainerPluginsRegistry.ApiInfo clusterEventProducerInfo = 
plugins.getPlugin(ClusterEventProducer.PLUGIN_NAME);
+    if (clusterEventProducerInfo != null) {
+      // the listener in ClusterSingletons already registered it
+      clusterEventProducer.setDelegate((ClusterEventProducer) 
clusterEventProducerInfo.getInstance());
+    } else {
+      // use the default NoOp impl
+    }
+    // transfer those listeners that were already registered to the initial 
impl
+    transferListeners(clusterEventProducer, plugins);
+    // install plugin registry listener
+    ContainerPluginsRegistry.PluginRegistryListener pluginListener = new 
ContainerPluginsRegistry.PluginRegistryListener() {
+      @Override
+      public void added(ContainerPluginsRegistry.ApiInfo plugin) {
+        if (plugin == null || plugin.getInstance() == null) {
+          return;
+        }
+        Object instance = plugin.getInstance();
+        if (instance instanceof ClusterEventListener) {
+          ClusterEventListener listener = (ClusterEventListener) instance;
+          clusterEventProducer.registerListener(listener);
+        } else if (instance instanceof ClusterEventProducer) {
+          // replace the existing impl
+          if (cc.getClusterEventProducer() instanceof 
DelegatingClusterEventProducer) {
+            ((DelegatingClusterEventProducer) cc.getClusterEventProducer())
+                .setDelegate((ClusterEventProducer) instance);
+          } else {
+            log.warn("Can't configure plugin-based ClusterEventProducer while 
CoreContainer is still loading - " +
+                " using existing implementation {}", 
cc.getClusterEventProducer().getClass().getName());
+          }
+        }
+      }
+
+      @Override
+      public void deleted(ContainerPluginsRegistry.ApiInfo plugin) {
+        if (plugin == null || plugin.getInstance() == null) {
+          return;
+        }
+        Object instance = plugin.getInstance();
+        if (instance instanceof ClusterEventListener) {
+          ClusterEventListener listener = (ClusterEventListener) instance;
+          clusterEventProducer.unregisterListener(listener);
+        } else if (instance instanceof ClusterEventProducer) {
+          // replace the existing impl with NoOp
+          if (cc.getClusterEventProducer() instanceof 
DelegatingClusterEventProducer) {
+            ((DelegatingClusterEventProducer) cc.getClusterEventProducer())
+                .setDelegate(new NoOpProducer(cc));
+          } else {
+            log.warn("Can't configure plugin-based ClusterEventProducer while 
CoreContainer is still loading - " +
+                " using existing implementation {}", 
cc.getClusterEventProducer().getClass().getName());
+          }
+        }
+      }
+
+      @Override
+      public void modified(ContainerPluginsRegistry.ApiInfo old, 
ContainerPluginsRegistry.ApiInfo replacement) {
+        added(replacement);
+        deleted(old);
+      }
+    };
+    plugins.registerListener(pluginListener);
+    created = true;
+    return clusterEventProducer;
+  }
+
+  private void transferListeners(ClusterEventProducer target, 
ContainerPluginsRegistry plugins) {

Review comment:
       Would there be a way to register listeners into an intermediate object 
(not tied to any specific producer), then have event producers feed events into 
this object (conceptually it would behave like a queue, but the implementation 
can be direct listener method calls). This would save having methods such as 
this one (and would not require having a producer in order to register 
listeners, if that helps...).




----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

For queries about this service, please contact Infrastructure at:
us...@infra.apache.org



---------------------------------------------------------------------
To unsubscribe, e-mail: issues-unsubscr...@lucene.apache.org
For additional commands, e-mail: issues-h...@lucene.apache.org

Reply via email to