You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ab...@apache.org on 2017/07/05 19:43:15 UTC

[1/5] lucene-solr:jira/solr-10996: SOLR-10996 Reduce confusion - use EventProcessor instead of TriggerListener.

Repository: lucene-solr
Updated Branches:
  refs/heads/jira/solr-10996 [created] b52e11480


SOLR-10996 Reduce confusion - use EventProcessor instead of TriggerListener.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/9e93a786
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/9e93a786
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/9e93a786

Branch: refs/heads/jira/solr-10996
Commit: 9e93a786939e8fb5cf361141c5017fe1e0a955bf
Parents: 8e0247e
Author: Andrzej Bialecki <ab...@apache.org>
Authored: Mon Jul 3 15:41:21 2017 +0200
Committer: Andrzej Bialecki <ab...@apache.org>
Committed: Mon Jul 3 15:41:21 2017 +0200

----------------------------------------------------------------------
 .../solr/cloud/autoscaling/AutoScaling.java     | 29 +++++++++++++++-----
 .../cloud/autoscaling/NodeAddedTrigger.java     | 20 +++++++-------
 .../solr/cloud/autoscaling/NodeLostTrigger.java | 20 +++++++-------
 .../cloud/autoscaling/ScheduledTriggers.java    |  4 +--
 .../cloud/autoscaling/NodeAddedTriggerTest.java | 16 +++++------
 .../cloud/autoscaling/NodeLostTriggerTest.java  | 16 +++++------
 6 files changed, 60 insertions(+), 45 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9e93a786/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
index cd65090..c1d676d 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
@@ -48,6 +48,21 @@ public class AutoScaling {
     AFTER_ACTION
   }
 
+  /**
+   * Implementation of this class is used for processing events generated by a trigger.
+   */
+  public interface EventProcessor {
+
+    /**
+     * This method is executed for events produced by {@link Trigger#run()}.
+     *
+     * @param event a subclass of {@link TriggerEvent}
+     * @return true if the processor was ready to perform actions on the event, false
+     * otherwise. If false was returned then callers should assume the event was discarded.
+     */
+    boolean process(TriggerEvent event);
+  }
+
   public interface TriggerListener {
     /**
      * This method is executed when a trigger is ready to fire.
@@ -69,8 +84,8 @@ public class AutoScaling {
   /**
    * Interface for a Solr trigger. Each trigger implements Runnable and Closeable interface. A trigger
    * is scheduled using a {@link java.util.concurrent.ScheduledExecutorService} so it is executed as
-   * per a configured schedule to check whether the trigger is ready to fire. The {@link Trigger#setListener(TriggerListener)}
-   * method should be used to set a callback listener which is fired by implementation of this class whenever
+   * per a configured schedule to check whether the trigger is ready to fire. The {@link Trigger#setProcessor(EventProcessor)}
+   * method should be used to set a processor which is used by implementation of this class whenever
    * ready.
    * <p>
    * As per the guarantees made by the {@link java.util.concurrent.ScheduledExecutorService} a trigger
@@ -79,7 +94,7 @@ public class AutoScaling {
    * which can be get/set by a different thread than the one executing the trigger. Therefore, implementations
    * should use appropriate synchronization around the listener.
    * <p>
-   * When a trigger is ready to fire, it calls the {@link TriggerListener#triggerFired(TriggerEvent)} event
+   * When a trigger is ready to fire, it calls the {@link EventProcessor#process(TriggerEvent)} event
    * with the proper trigger event object. If that method returns false then it should be interpreted to mean
    * that Solr is not ready to process this trigger event and therefore we should retain the state and fire
    * at the next invocation of the run() method.
@@ -107,11 +122,11 @@ public class AutoScaling {
     /** Actions to execute when event is fired. */
     List<TriggerAction> getActions();
 
-    /** Set event listener to call when event is fired. */
-    void setListener(TriggerListener listener);
+    /** Set event processor to call when event is fired. */
+    void setProcessor(EventProcessor processor);
 
-    /** Get event listener. */
-    TriggerListener getListener();
+    /** Get event processor. */
+    EventProcessor getProcessor();
 
     /** Return true when this trigger is closed and cannot be used. */
     boolean isClosed();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9e93a786/solr/core/src/java/org/apache/solr/cloud/autoscaling/NodeAddedTrigger.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/NodeAddedTrigger.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/NodeAddedTrigger.java
index 4162ef2..c51e586 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/NodeAddedTrigger.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/NodeAddedTrigger.java
@@ -52,7 +52,7 @@ public class NodeAddedTrigger extends TriggerBase {
   private final Map<String, Object> properties;
   private final CoreContainer container;
   private final List<TriggerAction> actions;
-  private final AtomicReference<AutoScaling.TriggerListener> listenerRef;
+  private final AtomicReference<AutoScaling.EventProcessor> processorRef;
   private final boolean enabled;
   private final int waitForSecond;
   private final AutoScaling.EventType eventType;
@@ -71,7 +71,7 @@ public class NodeAddedTrigger extends TriggerBase {
     this.properties = properties;
     this.container = container;
     this.timeSource = TimeSource.CURRENT_TIME;
-    this.listenerRef = new AtomicReference<>();
+    this.processorRef = new AtomicReference<>();
     List<Map<String, String>> o = (List<Map<String, String>>) properties.get("actions");
     if (o != null && !o.isEmpty()) {
       actions = new ArrayList<>(3);
@@ -119,13 +119,13 @@ public class NodeAddedTrigger extends TriggerBase {
   }
 
   @Override
-  public void setListener(AutoScaling.TriggerListener listener) {
-    listenerRef.set(listener);
+  public void setProcessor(AutoScaling.EventProcessor processor) {
+    processorRef.set(processor);
   }
 
   @Override
-  public AutoScaling.TriggerListener getListener() {
-    return listenerRef.get();
+  public AutoScaling.EventProcessor getProcessor() {
+    return processorRef.get();
   }
 
   @Override
@@ -254,10 +254,10 @@ public class NodeAddedTrigger extends TriggerBase {
         long now = timeSource.getTime();
         if (TimeUnit.SECONDS.convert(now - timeAdded, TimeUnit.NANOSECONDS) >= getWaitForSecond()) {
           // fire!
-          AutoScaling.TriggerListener listener = listenerRef.get();
-          if (listener != null) {
-            log.debug("NodeAddedTrigger {} firing registered listener for node: {} added at time {} , now: {}", name, nodeName, timeAdded, now);
-            if (listener.triggerFired(new NodeAddedEvent(getEventType(), getName(), timeAdded, nodeName))) {
+          AutoScaling.EventProcessor processor = processorRef.get();
+          if (processor != null) {
+            log.debug("NodeAddedTrigger {} firing registered processor for node: {} added at time {} , now: {}", name, nodeName, timeAdded, now);
+            if (processor.process(new NodeAddedEvent(getEventType(), getName(), timeAdded, nodeName))) {
               // remove from tracking set only if the fire was accepted
               it.remove();
               removeNodeAddedMarker(nodeName);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9e93a786/solr/core/src/java/org/apache/solr/cloud/autoscaling/NodeLostTrigger.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/NodeLostTrigger.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/NodeLostTrigger.java
index 9db388d..d762897 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/NodeLostTrigger.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/NodeLostTrigger.java
@@ -52,7 +52,7 @@ public class NodeLostTrigger extends TriggerBase {
   private final Map<String, Object> properties;
   private final CoreContainer container;
   private final List<TriggerAction> actions;
-  private final AtomicReference<AutoScaling.TriggerListener> listenerRef;
+  private final AtomicReference<AutoScaling.EventProcessor> processorRef;
   private final boolean enabled;
   private final int waitForSecond;
   private final AutoScaling.EventType eventType;
@@ -71,7 +71,7 @@ public class NodeLostTrigger extends TriggerBase {
     this.properties = properties;
     this.container = container;
     this.timeSource = TimeSource.CURRENT_TIME;
-    this.listenerRef = new AtomicReference<>();
+    this.processorRef = new AtomicReference<>();
     List<Map<String, String>> o = (List<Map<String, String>>) properties.get("actions");
     if (o != null && !o.isEmpty()) {
       actions = new ArrayList<>(3);
@@ -117,13 +117,13 @@ public class NodeLostTrigger extends TriggerBase {
   }
 
   @Override
-  public void setListener(AutoScaling.TriggerListener listener) {
-    listenerRef.set(listener);
+  public void setProcessor(AutoScaling.EventProcessor processor) {
+    processorRef.set(processor);
   }
 
   @Override
-  public AutoScaling.TriggerListener getListener() {
-    return listenerRef.get();
+  public AutoScaling.EventProcessor getProcessor() {
+    return processorRef.get();
   }
 
   @Override
@@ -250,10 +250,10 @@ public class NodeLostTrigger extends TriggerBase {
         Long timeRemoved = entry.getValue();
         if (TimeUnit.SECONDS.convert(timeSource.getTime() - timeRemoved, TimeUnit.NANOSECONDS) >= getWaitForSecond()) {
           // fire!
-          AutoScaling.TriggerListener listener = listenerRef.get();
-          if (listener != null) {
-            log.debug("NodeLostTrigger firing registered listener");
-            if (listener.triggerFired(new NodeLostEvent(getEventType(), getName(), timeRemoved, nodeName)))  {
+          AutoScaling.EventProcessor processor = processorRef.get();
+          if (processor != null) {
+            log.debug("NodeLostTrigger firing registered processor");
+            if (processor.process(new NodeLostEvent(getEventType(), getName(), timeRemoved, nodeName)))  {
               it.remove();
               removeNodeLostMarker(nodeName);
             }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9e93a786/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java
index a15b2d1..8c18da2 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java
@@ -127,7 +127,7 @@ public class ScheduledTriggers implements Closeable {
       scheduledTrigger.setReplay(false);
       scheduledTriggers.replace(newTrigger.getName(), scheduledTrigger);
     }
-    newTrigger.setListener(event -> {
+    newTrigger.setProcessor(event -> {
       ScheduledTrigger scheduledSource = scheduledTriggers.get(event.getSource());
       if (scheduledSource == null) {
         log.warn("Ignoring autoscaling event " + event + " because the source trigger: " + event.getSource() + " doesn't exist.");
@@ -303,7 +303,7 @@ public class ScheduledTriggers implements Closeable {
           while ((event = queue.peekEvent()) != null) {
             // override REPLAYING=true
             event.getProperties().put(TriggerEvent.REPLAYING, true);
-            if (! trigger.getListener().triggerFired(event)) {
+            if (! trigger.getProcessor().process(event)) {
               log.error("Failed to re-play event, discarding: " + event);
             }
             queue.pollEvent(); // always remove it from queue

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9e93a786/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java
index 1a43bd3..50f7e3d 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java
@@ -73,13 +73,13 @@ public class NodeAddedTriggerTest extends SolrCloudTestCase {
     Map<String, Object> props = createTriggerProps(waitForSeconds);
 
     try (NodeAddedTrigger trigger = new NodeAddedTrigger("node_added_trigger", props, container)) {
-      trigger.setListener(noFirstRunListener);
+      trigger.setProcessor(noFirstRunListener);
       trigger.run();
 
       JettySolrRunner newNode = cluster.startJettySolrRunner();
       AtomicBoolean fired = new AtomicBoolean(false);
       AtomicReference<TriggerEvent> eventRef = new AtomicReference<>();
-      trigger.setListener(event -> {
+      trigger.setProcessor(event -> {
         if (fired.compareAndSet(false, true)) {
           eventRef.set(event);
           long currentTimeNanos = timeSource.getTime();
@@ -112,12 +112,12 @@ public class NodeAddedTriggerTest extends SolrCloudTestCase {
     try (NodeAddedTrigger trigger = new NodeAddedTrigger("node_added_trigger", props, container)) {
       final long waitTime = 2;
       props.put("waitFor", waitTime);
-      trigger.setListener(noFirstRunListener);
+      trigger.setProcessor(noFirstRunListener);
       trigger.run();
 
       JettySolrRunner newNode = cluster.startJettySolrRunner();
       AtomicBoolean fired = new AtomicBoolean(false);
-      trigger.setListener(event -> {
+      trigger.setProcessor(event -> {
         if (fired.compareAndSet(false, true)) {
           long currentTimeNanos = timeSource.getTime();
           long eventTimeNanos = event.getEventTime();
@@ -196,14 +196,14 @@ public class NodeAddedTriggerTest extends SolrCloudTestCase {
     CoreContainer container = cluster.getJettySolrRunners().get(0).getCoreContainer();
     Map<String, Object> props = createTriggerProps(0);
     try (NodeAddedTrigger trigger = new NodeAddedTrigger("node_added_trigger", props, container)) {
-      trigger.setListener(noFirstRunListener);
+      trigger.setProcessor(noFirstRunListener);
       trigger.run(); // starts tracking live nodes
 
       JettySolrRunner newNode = cluster.startJettySolrRunner();
       AtomicInteger callCount = new AtomicInteger(0);
       AtomicBoolean fired = new AtomicBoolean(false);
 
-      trigger.setListener(event -> {
+      trigger.setProcessor(event -> {
         if (callCount.incrementAndGet() < 2) {
           return false;
         } else  {
@@ -232,7 +232,7 @@ public class NodeAddedTriggerTest extends SolrCloudTestCase {
     // add a new node but update the trigger before the waitFor period expires
     // and assert that the new trigger still fires
     NodeAddedTrigger trigger = new NodeAddedTrigger("node_added_trigger", props, container);
-    trigger.setListener(noFirstRunListener);
+    trigger.setProcessor(noFirstRunListener);
     trigger.run();
 
     JettySolrRunner newNode = cluster.startJettySolrRunner();
@@ -251,7 +251,7 @@ public class NodeAddedTriggerTest extends SolrCloudTestCase {
     try (NodeAddedTrigger newTrigger = new NodeAddedTrigger("node_added_trigger", props, container))  {
       AtomicBoolean fired = new AtomicBoolean(false);
       AtomicReference<TriggerEvent> eventRef = new AtomicReference<>();
-      newTrigger.setListener(event -> {
+      newTrigger.setProcessor(event -> {
         if (fired.compareAndSet(false, true)) {
           eventRef.set(event);
           long currentTimeNanos = timeSource.getTime();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/9e93a786/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java
index 82e1326..bb3339c 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java
@@ -74,14 +74,14 @@ public class NodeLostTriggerTest extends SolrCloudTestCase {
     Map<String, Object> props = createTriggerProps(waitForSeconds);
 
     try (NodeLostTrigger trigger = new NodeLostTrigger("node_lost_trigger", props, container)) {
-      trigger.setListener(noFirstRunListener);
+      trigger.setProcessor(noFirstRunListener);
       trigger.run();
       String lostNodeName = cluster.getJettySolrRunner(1).getNodeName();
       cluster.stopJettySolrRunner(1);
 
       AtomicBoolean fired = new AtomicBoolean(false);
       AtomicReference<TriggerEvent> eventRef = new AtomicReference<>();
-      trigger.setListener(event -> {
+      trigger.setProcessor(event -> {
         if (fired.compareAndSet(false, true)) {
           eventRef.set(event);
           long currentTimeNanos = timeSource.getTime();
@@ -115,13 +115,13 @@ public class NodeLostTriggerTest extends SolrCloudTestCase {
     try (NodeLostTrigger trigger = new NodeLostTrigger("node_lost_trigger", props, container)) {
       final long waitTime = 2;
       props.put("waitFor", waitTime);
-      trigger.setListener(noFirstRunListener);
+      trigger.setProcessor(noFirstRunListener);
       trigger.run();
 
       JettySolrRunner lostNode = cluster.getJettySolrRunner(1);
       lostNode.stop();
       AtomicBoolean fired = new AtomicBoolean(false);
-      trigger.setListener(event -> {
+      trigger.setProcessor(event -> {
         if (fired.compareAndSet(false, true)) {
           long currentTimeNanos = timeSource.getTime();
           long eventTimeNanos = event.getEventTime();
@@ -210,7 +210,7 @@ public class NodeLostTriggerTest extends SolrCloudTestCase {
     CoreContainer container = cluster.getJettySolrRunners().get(0).getCoreContainer();
     Map<String, Object> props = createTriggerProps(0);
     try (NodeLostTrigger trigger = new NodeLostTrigger("node_added_trigger", props, container)) {
-      trigger.setListener(noFirstRunListener);
+      trigger.setProcessor(noFirstRunListener);
 
       JettySolrRunner newNode = cluster.startJettySolrRunner();
       cluster.waitForAllNodes(5);
@@ -230,7 +230,7 @@ public class NodeLostTriggerTest extends SolrCloudTestCase {
       AtomicInteger callCount = new AtomicInteger(0);
       AtomicBoolean fired = new AtomicBoolean(false);
 
-      trigger.setListener(event -> {
+      trigger.setProcessor(event -> {
         if (callCount.incrementAndGet() < 2) {
           return false;
         } else  {
@@ -263,7 +263,7 @@ public class NodeLostTriggerTest extends SolrCloudTestCase {
     // and assert that the new trigger still fires
 
     NodeLostTrigger trigger = new NodeLostTrigger("node_lost_trigger", props, container);
-    trigger.setListener(noFirstRunListener);
+    trigger.setProcessor(noFirstRunListener);
     trigger.run();
 
     // stop the newly created node
@@ -291,7 +291,7 @@ public class NodeLostTriggerTest extends SolrCloudTestCase {
     try (NodeLostTrigger newTrigger = new NodeLostTrigger("node_lost_trigger", props, container)) {
       AtomicBoolean fired = new AtomicBoolean(false);
       AtomicReference<TriggerEvent> eventRef = new AtomicReference<>();
-      newTrigger.setListener(event -> {
+      newTrigger.setProcessor(event -> {
         if (fired.compareAndSet(false, true)) {
           eventRef.set(event);
           long currentTimeNanos = timeSource.getTime();


[2/5] lucene-solr:jira/solr-10996: WIP.

Posted by ab...@apache.org.
WIP.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/6a35a889
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/6a35a889
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/6a35a889

Branch: refs/heads/jira/solr-10996
Commit: 6a35a8896e9cdc065c7e2dd3c07f009243c981c1
Parents: 9e93a78
Author: Andrzej Bialecki <ab...@apache.org>
Authored: Tue Jul 4 10:01:49 2017 +0200
Committer: Andrzej Bialecki <ab...@apache.org>
Committed: Tue Jul 4 10:01:49 2017 +0200

----------------------------------------------------------------------
 .../solr/cloud/autoscaling/AutoScaling.java     | 22 ++---
 .../cloud/autoscaling/AutoScalingConfig.java    | 89 +++++++++++++++-----
 .../cloud/autoscaling/HttpTriggerListener.java  | 38 +++++++++
 .../cloud/autoscaling/LogTriggerListener.java   | 42 +++++++++
 .../autoscaling/AutoScalingHandlerTest.java     |  6 +-
 .../cloud/autoscaling/NodeAddedTriggerTest.java | 10 +--
 .../cloud/autoscaling/NodeLostTriggerTest.java  | 10 +--
 7 files changed, 172 insertions(+), 45 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6a35a889/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
index c1d676d..5f80f80 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
@@ -64,21 +64,17 @@ public class AutoScaling {
   }
 
   public interface TriggerListener {
+
+    void init(CoreContainer coreContainer);
+
     /**
-     * This method is executed when a trigger is ready to fire.
-     *
-     * @param event a subclass of {@link TriggerEvent}
-     * @return true if the listener was ready to perform actions on the event, false
-     * otherwise. If false was returned then callers should assume the event was discarded.
+     * This method is called when either a particular <code>stage</code> or
+     * <code>actionName</code> is reached during event processing.
+     * @param stage {@link TriggerStage} that this listener was registered for, or null
+     * @param actionName {@link TriggerAction} name that this listener was registered for, or null
+     * @param event current event being processed
      */
-    boolean triggerFired(TriggerEvent event);
-  }
-
-  public static class HttpCallbackListener implements TriggerListener {
-    @Override
-    public boolean triggerFired(TriggerEvent event) {
-      return true;
-    }
+    void onEvent(TriggerStage stage, String actionName, TriggerEvent event);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6a35a889/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java
index 5714ac9..753f6e9 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java
@@ -16,6 +16,8 @@
  */
 package org.apache.solr.cloud.autoscaling;
 
+import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -25,12 +27,15 @@ import java.util.Map;
 import org.apache.solr.client.solrj.cloud.autoscaling.Policy;
 import org.apache.solr.common.params.AutoScalingParams;
 import org.apache.solr.common.util.Utils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * Simple bean representation of <code>autoscaling.json</code>, which parses data
  * lazily.
  */
 public class AutoScalingConfig {
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
   private final Map<String, Object> jsonMap;
 
@@ -42,15 +47,26 @@ public class AutoScalingConfig {
    * Bean representation of {@link org.apache.solr.cloud.autoscaling.AutoScaling.TriggerListener} config.
    */
   public static class ListenerConfig {
-    public String trigger;
-    public List<String> stages;
-    public String listenerClass;
-    public List<Map<String, String>> beforeActions;
-    public List<Map<String, String>> afterActions;
+    public final String trigger;
+    public final List<AutoScaling.TriggerStage> stages;
+    public final String listenerClass;
+    public final List<Map<String, String>> beforeActions;
+    public final List<Map<String, String>> afterActions;
+    public final Map<String, Object> properties = new HashMap<>();
 
     public ListenerConfig(Map<String, Object> properties) {
+      this.properties.putAll(properties);
       trigger = (String)properties.get(AutoScalingParams.TRIGGER);
-      stages = (List<String>)properties.getOrDefault(AutoScalingParams.STAGE, Collections.emptyList());
+      List<String> stageNames = (List<String>)properties.getOrDefault(AutoScalingParams.STAGE, Collections.emptyList());
+      stages = new ArrayList<>(stageNames.size());
+      for (String name : stageNames) {
+        try {
+          AutoScaling.TriggerStage stage = AutoScaling.TriggerStage.valueOf(name.toUpperCase(Locale.ROOT));
+          stages.add(stage);
+        } catch (Exception e) {
+          LOG.warn("Invalid stage name '" + name + "' in listener config, skipping: " + properties);
+        }
+      }
       listenerClass = (String)properties.get(AutoScalingParams.CLASS);
       beforeActions = (List<Map<String, String>>)properties.getOrDefault(AutoScalingParams.BEFORE_ACTION, Collections.emptyList());
       afterActions = (List<Map<String, String>>)properties.getOrDefault(AutoScalingParams.AFTER_ACTION, Collections.emptyList());
@@ -80,6 +96,13 @@ public class AutoScalingConfig {
   }
 
   /**
+   * Return the original JSON map representation that was used for building this config.
+   */
+  public Map<String, Object> getJsonMap() {
+    return jsonMap;
+  }
+
+  /**
    * Get {@link Policy} configuration.
    */
   public Policy getPolicy() {
@@ -108,6 +131,26 @@ public class AutoScalingConfig {
   }
 
   /**
+   * Check whether triggers for specific event type exist.
+   * @param types list of event types
+   * @return true if there's at least one trigger matching at least one event type,
+   * false otherwise,
+   */
+  public boolean hasTriggerForEvents(AutoScaling.EventType... types) {
+    if (types == null || types.length == 0) {
+      return false;
+    }
+    for (TriggerConfig config : getTriggerConfigs().values()) {
+      for (AutoScaling.EventType type : types) {
+        if (config.eventType.equals(type)) {
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+  /**
    * Get listener configurations.
    */
   public Map<String, ListenerConfig> getListenerConfigs() {
@@ -126,22 +169,30 @@ public class AutoScalingConfig {
   }
 
   /**
-   * Check whether triggers for specific event type exist.
-   * @param types list of event types
-   * @return true if there's at least one trigger matching at least one event type,
-   * false otherwise,
+   * Get listeners for a specific trigger name.
+   * @param triggerName name of the trigger
+   * @return a map where keys are the stages and values are the list of listener configs that apply to a
+   * particular triggerName and stage.
    */
-  public boolean hasTriggerForEvents(AutoScaling.EventType... types) {
-    if (types == null || types.length == 0) {
-      return false;
-    }
-    for (TriggerConfig config : getTriggerConfigs().values()) {
-      for (AutoScaling.EventType type : types) {
-        if (config.eventType.equals(type)) {
-          return true;
+  public Map<AutoScaling.TriggerStage, List<ListenerConfig>> getListenerConfigsForTrigger(String triggerName) {
+    Map<String, ListenerConfig> configs = getListenerConfigs();
+    if (configs.isEmpty()) {
+      return Collections.emptyMap();
+    } else {
+      Map<AutoScaling.TriggerStage, List<ListenerConfig>> res = new HashMap<>();
+      for (ListenerConfig cfg : configs.values()) {
+        if (triggerName.equals(cfg.trigger)) {
+          for (AutoScaling.TriggerStage stage : cfg.stages) {
+            List<ListenerConfig> cfgsPerStage = res.get(stage);
+            if (cfgsPerStage == null) {
+              cfgsPerStage = new ArrayList<>();
+              res.put(stage, cfgsPerStage);
+            }
+            cfgsPerStage.add(cfg);
+          }
         }
       }
+      return res;
     }
-    return false;
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6a35a889/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java
new file mode 100644
index 0000000..57a9c31
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.cloud.autoscaling;
+
+import org.apache.http.client.HttpClient;
+import org.apache.solr.core.CoreContainer;
+
+/**
+ * Simple HTTP callback that sends trigger events as JSON.
+ */
+public class HttpTriggerListener implements AutoScaling.TriggerListener {
+
+  private HttpClient httpClient;
+
+  @Override
+  public void init(CoreContainer coreContainer) {
+    httpClient = coreContainer.getUpdateShardHandler().getHttpClient();
+  }
+
+  @Override
+  public void onEvent(AutoScaling.TriggerStage stage, String actionName, TriggerEvent event) {
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6a35a889/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java
new file mode 100644
index 0000000..a4d4fdd
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.solr.cloud.autoscaling;
+
+import java.lang.invoke.MethodHandles;
+
+import org.apache.solr.core.CoreContainer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implementation of {@link org.apache.solr.cloud.autoscaling.AutoScaling.TriggerListener} that reports
+ * events to a log.
+ */
+public class LogTriggerListener implements AutoScaling.TriggerListener {
+  private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
+
+  @Override
+  public void init(CoreContainer coreContainer) {
+
+  }
+
+  @Override
+  public void onEvent(AutoScaling.TriggerStage stage, String actionName, TriggerEvent event) {
+    LOG.info("stage={}, actionName={}, event={}", stage, actionName, event);
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6a35a889/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
index 06c2920..be7fb0c 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
@@ -356,7 +356,7 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
         "'trigger' : 'node_lost_trigger'," +
         "'stage' : ['STARTED','ABORTED','SUCCEEDED']," +
         "'beforeAction' : 'execute_plan'," +
-        "'class' : 'org.apache.solr.cloud.autoscaling.AutoScaling$HttpCallbackListener'," +
+        "'class' : 'org.apache.solr.cloud.autoscaling.AutoScaling$HttpTriggerListener'," +
         "'url' : 'http://xyz.com/on_node_lost?node={$LOST_NODE_NAME}'" +
         "}" +
         "}";
@@ -371,7 +371,7 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
     assertTrue(listeners.containsKey("xyz"));
     Map<String, Object> xyzListener = (Map<String, Object>) listeners.get("xyz");
     assertEquals(5, xyzListener.size());
-    assertEquals("org.apache.solr.cloud.autoscaling.AutoScaling$HttpCallbackListener", xyzListener.get("class").toString());
+    assertEquals("org.apache.solr.cloud.autoscaling.AutoScaling$HttpTriggerListener", xyzListener.get("class").toString());
 
     String removeTriggerCommand = "{" +
         "'remove-trigger' : {" +
@@ -422,7 +422,7 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
         "'trigger' : 'node_lost_trigger'," +
         "'stage' : ['STARTED','ABORTED','SUCCEEDED']," +
         "'beforeAction' : 'execute_plan'," +
-        "'class' : 'org.apache.solr.cloud.autoscaling.AutoScaling$HttpCallbackListener'," +
+        "'class' : 'org.apache.solr.cloud.autoscaling.AutoScaling$HttpTriggerListener'," +
         "'url' : 'http://xyz.com/on_node_lost?node={$LOST_NODE_NAME}'" +
         "}" +
         "}";

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6a35a889/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java
index 50f7e3d..a2beed4 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeAddedTriggerTest.java
@@ -43,7 +43,7 @@ public class NodeAddedTriggerTest extends SolrCloudTestCase {
   private static AtomicBoolean actionInitCalled = new AtomicBoolean(false);
   private static AtomicBoolean actionCloseCalled = new AtomicBoolean(false);
 
-  private AutoScaling.TriggerListener noFirstRunListener = event -> {
+  private AutoScaling.EventProcessor noFirstRunProcessor = event -> {
     fail("Did not expect the listener to fire on first run!");
     return true;
   };
@@ -73,7 +73,7 @@ public class NodeAddedTriggerTest extends SolrCloudTestCase {
     Map<String, Object> props = createTriggerProps(waitForSeconds);
 
     try (NodeAddedTrigger trigger = new NodeAddedTrigger("node_added_trigger", props, container)) {
-      trigger.setProcessor(noFirstRunListener);
+      trigger.setProcessor(noFirstRunProcessor);
       trigger.run();
 
       JettySolrRunner newNode = cluster.startJettySolrRunner();
@@ -112,7 +112,7 @@ public class NodeAddedTriggerTest extends SolrCloudTestCase {
     try (NodeAddedTrigger trigger = new NodeAddedTrigger("node_added_trigger", props, container)) {
       final long waitTime = 2;
       props.put("waitFor", waitTime);
-      trigger.setProcessor(noFirstRunListener);
+      trigger.setProcessor(noFirstRunProcessor);
       trigger.run();
 
       JettySolrRunner newNode = cluster.startJettySolrRunner();
@@ -196,7 +196,7 @@ public class NodeAddedTriggerTest extends SolrCloudTestCase {
     CoreContainer container = cluster.getJettySolrRunners().get(0).getCoreContainer();
     Map<String, Object> props = createTriggerProps(0);
     try (NodeAddedTrigger trigger = new NodeAddedTrigger("node_added_trigger", props, container)) {
-      trigger.setProcessor(noFirstRunListener);
+      trigger.setProcessor(noFirstRunProcessor);
       trigger.run(); // starts tracking live nodes
 
       JettySolrRunner newNode = cluster.startJettySolrRunner();
@@ -232,7 +232,7 @@ public class NodeAddedTriggerTest extends SolrCloudTestCase {
     // add a new node but update the trigger before the waitFor period expires
     // and assert that the new trigger still fires
     NodeAddedTrigger trigger = new NodeAddedTrigger("node_added_trigger", props, container);
-    trigger.setProcessor(noFirstRunListener);
+    trigger.setProcessor(noFirstRunProcessor);
     trigger.run();
 
     JettySolrRunner newNode = cluster.startJettySolrRunner();

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/6a35a889/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java
index bb3339c..2492ce2 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/NodeLostTriggerTest.java
@@ -43,7 +43,7 @@ public class NodeLostTriggerTest extends SolrCloudTestCase {
   private static AtomicBoolean actionInitCalled = new AtomicBoolean(false);
   private static AtomicBoolean actionCloseCalled = new AtomicBoolean(false);
 
-  private AutoScaling.TriggerListener noFirstRunListener = event -> {
+  private AutoScaling.EventProcessor noFirstRunProcessor = event -> {
     fail("Did not expect the listener to fire on first run!");
     return true;
   };
@@ -74,7 +74,7 @@ public class NodeLostTriggerTest extends SolrCloudTestCase {
     Map<String, Object> props = createTriggerProps(waitForSeconds);
 
     try (NodeLostTrigger trigger = new NodeLostTrigger("node_lost_trigger", props, container)) {
-      trigger.setProcessor(noFirstRunListener);
+      trigger.setProcessor(noFirstRunProcessor);
       trigger.run();
       String lostNodeName = cluster.getJettySolrRunner(1).getNodeName();
       cluster.stopJettySolrRunner(1);
@@ -115,7 +115,7 @@ public class NodeLostTriggerTest extends SolrCloudTestCase {
     try (NodeLostTrigger trigger = new NodeLostTrigger("node_lost_trigger", props, container)) {
       final long waitTime = 2;
       props.put("waitFor", waitTime);
-      trigger.setProcessor(noFirstRunListener);
+      trigger.setProcessor(noFirstRunProcessor);
       trigger.run();
 
       JettySolrRunner lostNode = cluster.getJettySolrRunner(1);
@@ -210,7 +210,7 @@ public class NodeLostTriggerTest extends SolrCloudTestCase {
     CoreContainer container = cluster.getJettySolrRunners().get(0).getCoreContainer();
     Map<String, Object> props = createTriggerProps(0);
     try (NodeLostTrigger trigger = new NodeLostTrigger("node_added_trigger", props, container)) {
-      trigger.setProcessor(noFirstRunListener);
+      trigger.setProcessor(noFirstRunProcessor);
 
       JettySolrRunner newNode = cluster.startJettySolrRunner();
       cluster.waitForAllNodes(5);
@@ -263,7 +263,7 @@ public class NodeLostTriggerTest extends SolrCloudTestCase {
     // and assert that the new trigger still fires
 
     NodeLostTrigger trigger = new NodeLostTrigger("node_lost_trigger", props, container);
-    trigger.setProcessor(noFirstRunListener);
+    trigger.setProcessor(noFirstRunProcessor);
     trigger.run();
 
     // stop the newly created node


[3/5] lucene-solr:jira/solr-10996: SOLR-10996: Implement TriggerListener hooks and initializations.

Posted by ab...@apache.org.
SOLR-10996: Implement TriggerListener hooks and initializations.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/f17f3e6a
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/f17f3e6a
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/f17f3e6a

Branch: refs/heads/jira/solr-10996
Commit: f17f3e6a2b1da75ca95ee282fa0e1fb5b8d933e1
Parents: 6a35a88
Author: Andrzej Bialecki <ab...@apache.org>
Authored: Wed Jul 5 14:35:46 2017 +0200
Committer: Andrzej Bialecki <ab...@apache.org>
Committed: Wed Jul 5 14:35:46 2017 +0200

----------------------------------------------------------------------
 .../solr/cloud/autoscaling/AutoScaling.java     |  19 ++-
 .../cloud/autoscaling/AutoScalingConfig.java    |  57 +++-----
 .../cloud/autoscaling/AutoScalingHandler.java   |   2 +-
 .../cloud/autoscaling/HttpTriggerListener.java  |  18 ++-
 .../cloud/autoscaling/LogTriggerListener.java   |  19 ++-
 .../autoscaling/OverseerTriggerThread.java      |  24 ++--
 .../cloud/autoscaling/ScheduledTriggers.java    | 136 ++++++++++++++++++-
 .../autoscaling/AutoScalingHandlerTest.java     |   4 +-
 8 files changed, 212 insertions(+), 67 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f17f3e6a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
index 5f80f80..c1a1169 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
@@ -38,7 +38,7 @@ public class AutoScaling {
     INDEXRATE
   }
 
-  public enum TriggerStage {
+  public enum EventProcessorStage {
     WAITING,
     STARTED,
     ABORTED,
@@ -49,7 +49,7 @@ public class AutoScaling {
   }
 
   /**
-   * Implementation of this class is used for processing events generated by a trigger.
+   * Implementation of this interface is used for processing events generated by a trigger.
    */
   public interface EventProcessor {
 
@@ -63,18 +63,25 @@ public class AutoScaling {
     boolean process(TriggerEvent event);
   }
 
-  public interface TriggerListener {
+  /**
+   * Implementations of this interface are notified of stages in event processing that they were
+   * registered for. Note: instances may be closed and re-created on each auto-scaling config update.
+   */
+  public interface TriggerListener extends Closeable {
+
+    void init(CoreContainer coreContainer, AutoScalingConfig.TriggerListenerConfig config);
 
-    void init(CoreContainer coreContainer);
+    AutoScalingConfig.TriggerListenerConfig getTriggerListenerConfig();
 
     /**
      * This method is called when either a particular <code>stage</code> or
      * <code>actionName</code> is reached during event processing.
-     * @param stage {@link TriggerStage} that this listener was registered for, or null
+     * @param stage {@link EventProcessorStage} that this listener was registered for, or null
      * @param actionName {@link TriggerAction} name that this listener was registered for, or null
      * @param event current event being processed
+     * @param message optional message
      */
-    void onEvent(TriggerStage stage, String actionName, TriggerEvent event);
+    void onEvent(EventProcessorStage stage, String actionName, TriggerEvent event, String message);
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f17f3e6a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java
index 753f6e9..f2c251c 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java
@@ -20,9 +20,11 @@ import java.lang.invoke.MethodHandles;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Set;
 
 import org.apache.solr.client.solrj.cloud.autoscaling.Policy;
 import org.apache.solr.common.params.AutoScalingParams;
@@ -41,35 +43,35 @@ public class AutoScalingConfig {
 
   private Policy policy;
   private Map<String, TriggerConfig> triggers;
-  private Map<String, ListenerConfig> listeners;
+  private Map<String, TriggerListenerConfig> listeners;
 
   /**
    * Bean representation of {@link org.apache.solr.cloud.autoscaling.AutoScaling.TriggerListener} config.
    */
-  public static class ListenerConfig {
+  public static class TriggerListenerConfig {
     public final String trigger;
-    public final List<AutoScaling.TriggerStage> stages;
+    public final Set<AutoScaling.EventProcessorStage> stages;
     public final String listenerClass;
-    public final List<Map<String, String>> beforeActions;
-    public final List<Map<String, String>> afterActions;
+    public final Set<String> beforeActions;
+    public final Set<String> afterActions;
     public final Map<String, Object> properties = new HashMap<>();
 
-    public ListenerConfig(Map<String, Object> properties) {
+    public TriggerListenerConfig(Map<String, Object> properties) {
       this.properties.putAll(properties);
       trigger = (String)properties.get(AutoScalingParams.TRIGGER);
       List<String> stageNames = (List<String>)properties.getOrDefault(AutoScalingParams.STAGE, Collections.emptyList());
-      stages = new ArrayList<>(stageNames.size());
+      stages = new HashSet<>(stageNames.size());
       for (String name : stageNames) {
         try {
-          AutoScaling.TriggerStage stage = AutoScaling.TriggerStage.valueOf(name.toUpperCase(Locale.ROOT));
+          AutoScaling.EventProcessorStage stage = AutoScaling.EventProcessorStage.valueOf(name.toUpperCase(Locale.ROOT));
           stages.add(stage);
         } catch (Exception e) {
           LOG.warn("Invalid stage name '" + name + "' in listener config, skipping: " + properties);
         }
       }
       listenerClass = (String)properties.get(AutoScalingParams.CLASS);
-      beforeActions = (List<Map<String, String>>)properties.getOrDefault(AutoScalingParams.BEFORE_ACTION, Collections.emptyList());
-      afterActions = (List<Map<String, String>>)properties.getOrDefault(AutoScalingParams.AFTER_ACTION, Collections.emptyList());
+      beforeActions = new HashSet<>((List<String>)properties.getOrDefault(AutoScalingParams.BEFORE_ACTION, Collections.emptyList()));
+      afterActions = new HashSet<>((List<String>)properties.getOrDefault(AutoScalingParams.AFTER_ACTION, Collections.emptyList()));
     }
   }
 
@@ -87,6 +89,10 @@ public class AutoScalingConfig {
     }
   }
 
+  public AutoScalingConfig(byte[] utf8) {
+    this(utf8 != null && utf8.length > 0 ? (Map<String, Object>)Utils.fromJSON(utf8) : Collections.emptyMap());
+  }
+
   /**
    * Construct from a JSON map representation.
    * @param jsonMap
@@ -153,7 +159,7 @@ public class AutoScalingConfig {
   /**
    * Get listener configurations.
    */
-  public Map<String, ListenerConfig> getListenerConfigs() {
+  public Map<String, TriggerListenerConfig> getTriggerListenerConfigs() {
     if (listeners == null) {
       Map<String, Object> map = (Map<String, Object>)jsonMap.get("listeners");
       if (map == null) {
@@ -161,38 +167,11 @@ public class AutoScalingConfig {
       } else {
         listeners = new HashMap<>(map.size());
         for (Map.Entry<String, Object> entry : map.entrySet()) {
-          listeners.put(entry.getKey(), new ListenerConfig((Map<String, Object>)entry.getValue()));
+          listeners.put(entry.getKey(), new TriggerListenerConfig((Map<String, Object>)entry.getValue()));
         }
       }
     }
     return listeners;
   }
 
-  /**
-   * Get listeners for a specific trigger name.
-   * @param triggerName name of the trigger
-   * @return a map where keys are the stages and values are the list of listener configs that apply to a
-   * particular triggerName and stage.
-   */
-  public Map<AutoScaling.TriggerStage, List<ListenerConfig>> getListenerConfigsForTrigger(String triggerName) {
-    Map<String, ListenerConfig> configs = getListenerConfigs();
-    if (configs.isEmpty()) {
-      return Collections.emptyMap();
-    } else {
-      Map<AutoScaling.TriggerStage, List<ListenerConfig>> res = new HashMap<>();
-      for (ListenerConfig cfg : configs.values()) {
-        if (triggerName.equals(cfg.trigger)) {
-          for (AutoScaling.TriggerStage stage : cfg.stages) {
-            List<ListenerConfig> cfgsPerStage = res.get(stage);
-            if (cfgsPerStage == null) {
-              cfgsPerStage = new ArrayList<>();
-              res.put(stage, cfgsPerStage);
-            }
-            cfgsPerStage.add(cfg);
-          }
-        }
-      }
-      return res;
-    }
-  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f17f3e6a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java
index ed29542..5f877b8 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java
@@ -379,7 +379,7 @@ public class AutoScalingHandler extends RequestHandlerBase implements Permission
 
     for (String stage : stageNames) {
       try {
-        AutoScaling.TriggerStage.valueOf(stage);
+        AutoScaling.EventProcessorStage.valueOf(stage);
       } catch (IllegalArgumentException e) {
         throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Invalid stage name: " + stage);
       }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f17f3e6a/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java
index 57a9c31..84cccf0 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java
@@ -16,6 +16,8 @@
  */
 package org.apache.solr.cloud.autoscaling;
 
+import java.io.IOException;
+
 import org.apache.http.client.HttpClient;
 import org.apache.solr.core.CoreContainer;
 
@@ -25,14 +27,26 @@ import org.apache.solr.core.CoreContainer;
 public class HttpTriggerListener implements AutoScaling.TriggerListener {
 
   private HttpClient httpClient;
+  private AutoScalingConfig.TriggerListenerConfig config;
 
   @Override
-  public void init(CoreContainer coreContainer) {
+  public void init(CoreContainer coreContainer, AutoScalingConfig.TriggerListenerConfig config) {
+    this.config = config;
     httpClient = coreContainer.getUpdateShardHandler().getHttpClient();
   }
 
   @Override
-  public void onEvent(AutoScaling.TriggerStage stage, String actionName, TriggerEvent event) {
+  public void onEvent(AutoScaling.EventProcessorStage stage, String actionName, TriggerEvent event, String message) {
+
+  }
+
+  @Override
+  public AutoScalingConfig.TriggerListenerConfig getTriggerListenerConfig() {
+    return config;
+  }
+
+  @Override
+  public void close() throws IOException {
 
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f17f3e6a/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java
index a4d4fdd..2246b73 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java
@@ -17,6 +17,7 @@
 
 package org.apache.solr.cloud.autoscaling;
 
+import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 
 import org.apache.solr.core.CoreContainer;
@@ -30,13 +31,25 @@ import org.slf4j.LoggerFactory;
 public class LogTriggerListener implements AutoScaling.TriggerListener {
   private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
+  private AutoScalingConfig.TriggerListenerConfig config;
+
   @Override
-  public void init(CoreContainer coreContainer) {
+  public void init(CoreContainer coreContainer, AutoScalingConfig.TriggerListenerConfig config) {
+    this.config = config;
+  }
 
+  @Override
+  public AutoScalingConfig.TriggerListenerConfig getTriggerListenerConfig() {
+    return config;
   }
 
   @Override
-  public void onEvent(AutoScaling.TriggerStage stage, String actionName, TriggerEvent event) {
-    LOG.info("stage={}, actionName={}, event={}", stage, actionName, event);
+  public void onEvent(AutoScaling.EventProcessorStage stage, String actionName, TriggerEvent event, String message) {
+    LOG.info("stage={}, actionName={}, event={}, messsage={}", stage, actionName, event, message);
+  }
+
+  @Override
+  public void close() throws IOException {
+
   }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f17f3e6a/solr/core/src/java/org/apache/solr/cloud/autoscaling/OverseerTriggerThread.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/OverseerTriggerThread.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/OverseerTriggerThread.java
index d7fc47e..3666e1b 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/OverseerTriggerThread.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/OverseerTriggerThread.java
@@ -37,6 +37,7 @@ import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.cloud.ZooKeeperException;
 import org.apache.solr.common.params.AutoScalingParams;
 import org.apache.solr.common.util.IOUtils;
+import org.apache.solr.common.util.Utils;
 import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.WatchedEvent;
 import org.apache.zookeeper.Watcher;
@@ -75,6 +76,8 @@ public class OverseerTriggerThread implements Runnable, Closeable {
 
   private boolean isClosed = false;
 
+  private AutoScalingConfig autoScalingConfig;
+
   public OverseerTriggerThread(ZkController zkController) {
     this.zkController = zkController;
     zkStateReader = zkController.getZkStateReader();
@@ -163,6 +166,9 @@ public class OverseerTriggerThread implements Runnable, Closeable {
         break;
       }
 
+      // update the current config
+      scheduledTriggers.setAutoScalingConfig(autoScalingConfig);
+
       Set<String> managedTriggerNames = scheduledTriggers.getScheduledTriggerNames();
       // remove the triggers which are no longer active
       for (String managedTriggerName : managedTriggerNames) {
@@ -265,8 +271,9 @@ public class OverseerTriggerThread implements Runnable, Closeable {
         // protect against reordered watcher fires by ensuring that we only move forward
         return;
       }
+      autoScalingConfig = new AutoScalingConfig(data);
       znodeVersion = stat.getVersion();
-      Map<String, AutoScaling.Trigger> triggerMap = loadTriggers(triggerFactory, data);
+      Map<String, AutoScaling.Trigger> triggerMap = loadTriggers(triggerFactory, autoScalingConfig);
 
       // remove all active triggers that have been removed from ZK
       Set<String> trackingKeySet = activeTriggers.keySet();
@@ -288,22 +295,19 @@ public class OverseerTriggerThread implements Runnable, Closeable {
     }
   }
 
-  private static Map<String, AutoScaling.Trigger> loadTriggers(AutoScaling.TriggerFactory triggerFactory, byte[] data) {
-    ZkNodeProps loaded = ZkNodeProps.load(data);
-    Map<String, Object> triggers = (Map<String, Object>) loaded.get("triggers");
-
+  private static Map<String, AutoScaling.Trigger> loadTriggers(AutoScaling.TriggerFactory triggerFactory, AutoScalingConfig autoScalingConfig) {
+    Map<String, AutoScalingConfig.TriggerConfig> triggers = autoScalingConfig.getTriggerConfigs();
     if (triggers == null) {
       return Collections.emptyMap();
     }
 
     Map<String, AutoScaling.Trigger> triggerMap = new HashMap<>(triggers.size());
 
-    for (Map.Entry<String, Object> entry : triggers.entrySet()) {
-      Map<String, Object> props = (Map<String, Object>) entry.getValue();
-      String event = (String) props.get(AutoScalingParams.EVENT);
-      AutoScaling.EventType eventType = AutoScaling.EventType.valueOf(event.toUpperCase(Locale.ROOT));
+    for (Map.Entry<String, AutoScalingConfig.TriggerConfig> entry : triggers.entrySet()) {
+      AutoScalingConfig.TriggerConfig cfg = entry.getValue();
+      AutoScaling.EventType eventType = cfg.eventType;
       String triggerName = entry.getKey();
-      triggerMap.put(triggerName, triggerFactory.create(eventType, triggerName, props));
+      triggerMap.put(triggerName, triggerFactory.create(eventType, triggerName, cfg.properties));
     }
     return triggerMap;
   }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f17f3e6a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java
index 8c18da2..1be62a4 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java
@@ -34,6 +34,7 @@ import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.ReentrantLock;
 
 import org.apache.lucene.store.AlreadyClosedException;
 import org.apache.solr.cloud.ActionThrottle;
@@ -85,6 +86,10 @@ public class ScheduledTriggers implements Closeable {
 
   private final CoreContainer coreContainer;
 
+  private final TriggerListeners listeners;
+
+  private AutoScalingConfig autoScalingConfig;
+
   public ScheduledTriggers(ZkController zkController) {
     // todo make the core pool size configurable
     // it is important to use more than one because a time taking trigger can starve other scheduled triggers
@@ -98,9 +103,20 @@ public class ScheduledTriggers implements Closeable {
     actionExecutor = ExecutorUtil.newMDCAwareSingleThreadExecutor(new DefaultSolrThreadFactory("AutoscalingActionExecutor"));
     // todo make the wait time configurable
     actionThrottle = new ActionThrottle("action", DEFAULT_MIN_MS_BETWEEN_ACTIONS);
-    this.coreContainer = zkController.getCoreContainer();
-    this.zkClient = zkController.getZkClient();
+    coreContainer = zkController.getCoreContainer();
+    zkClient = zkController.getZkClient();
     queueStats = new Overseer.Stats();
+    listeners = new TriggerListeners();
+  }
+
+  /**
+   * Set the current autoscaling config. This is invoked by {@link OverseerTriggerThread} when autoscaling.json is updated,
+   * and it re-initializes trigger listeners.
+   * @param autoScalingConfig current autoscaling.json
+   */
+  public void setAutoScalingConfig(AutoScalingConfig autoScalingConfig) {
+    this.autoScalingConfig = autoScalingConfig;
+    listeners.setAutoScalingConfig(autoScalingConfig);
   }
 
   /**
@@ -128,15 +144,20 @@ public class ScheduledTriggers implements Closeable {
       scheduledTriggers.replace(newTrigger.getName(), scheduledTrigger);
     }
     newTrigger.setProcessor(event -> {
+      listeners.fireListeners(event.getSource(), AutoScaling.EventProcessorStage.STARTED, null, event, null);
       ScheduledTrigger scheduledSource = scheduledTriggers.get(event.getSource());
       if (scheduledSource == null) {
-        log.warn("Ignoring autoscaling event " + event + " because the source trigger: " + event.getSource() + " doesn't exist.");
+        String msg = String.format("Ignoring autoscaling event {} because the source trigger: {} doesn't exist.", event.toString(), event.getSource());
+        listeners.fireListeners(event.getSource(), AutoScaling.EventProcessorStage.FAILED, null, event, msg);
+        log.warn(msg);
         return false;
       }
       boolean replaying = event.getProperty(TriggerEvent.REPLAYING) != null ? (Boolean)event.getProperty(TriggerEvent.REPLAYING) : false;
       AutoScaling.Trigger source = scheduledSource.trigger;
       if (source.isClosed()) {
-        log.warn("Ignoring autoscaling event " + event + " because the source trigger: " + source + " has already been closed");
+        String msg = String.format("Ignoring autoscaling event {} because the source trigger: {} has already been closed", event.toString(), source);
+        listeners.fireListeners(event.getSource(), AutoScaling.EventProcessorStage.ABORTED, null, event, msg);
+        log.warn(msg);
         // we do not want to lose this event just because the trigger was closed, perhaps a replacement will need it
         return false;
       }
@@ -158,17 +179,21 @@ public class ScheduledTriggers implements Closeable {
               actionThrottle.markAttemptingAction();
               ActionContext actionContext = new ActionContext(coreContainer, newTrigger, new HashMap<>());
               for (TriggerAction action : actions) {
+                listeners.fireListeners(event.getSource(), AutoScaling.EventProcessorStage.BEFORE_ACTION, action.getName(), event, null);
                 try {
                   action.process(event, actionContext);
                 } catch (Exception e) {
+                  listeners.fireListeners(event.getSource(), AutoScaling.EventProcessorStage.FAILED, action.getName(), event, null);
                   log.error("Error executing action: " + action.getName() + " for trigger event: " + event, e);
                   throw e;
                 }
+                listeners.fireListeners(event.getSource(), AutoScaling.EventProcessorStage.AFTER_ACTION, action.getName(), event, null);
               }
               if (enqueued) {
                 TriggerEvent ev = scheduledTrigger.dequeue();
                 assert ev.getId().equals(event.getId());
               }
+              listeners.fireListeners(event.getSource(), AutoScaling.EventProcessorStage.SUCCEEDED, null, event, null);
             } finally {
               hasPendingActions.set(false);
             }
@@ -181,10 +206,12 @@ public class ScheduledTriggers implements Closeable {
               + " is broken! Expected event=" + event + " but got " + ev);
             }
           }
+          listeners.fireListeners(event.getSource(), AutoScaling.EventProcessorStage.SUCCEEDED, null, event, null);
           hasPendingActions.set(false);
         }
         return true;
       } else {
+        listeners.fireListeners(event.getSource(), AutoScaling.EventProcessorStage.WAITING, null, event, null);
         // there is an action in the queue and we don't want to enqueue another until it is complete
         return false;
       }
@@ -252,6 +279,7 @@ public class ScheduledTriggers implements Closeable {
     // guarantee about cluster state
     scheduledThreadPoolExecutor.shutdownNow();
     actionExecutor.shutdownNow();
+    listeners.close();
   }
 
   private class ScheduledTrigger implements Runnable, Closeable {
@@ -339,4 +367,104 @@ public class ScheduledTriggers implements Closeable {
       IOUtils.closeQuietly(trigger);
     }
   }
+
+  private class TriggerListeners {
+    Map<String, Map<AutoScaling.EventProcessorStage, List<AutoScaling.TriggerListener>>> listenerPerStage = new HashMap<>();
+    List<AutoScaling.TriggerListener> listeners = new ArrayList<>();
+    ReentrantLock updateLock = new ReentrantLock();
+
+    void setAutoScalingConfig(AutoScalingConfig autoScalingConfig) {
+      updateLock.lock();
+      try {
+        // close previous instances
+        close();
+        // instantiate only those for existing triggers
+        Set<String> triggerNames = autoScalingConfig.getTriggerConfigs().keySet();
+        Map<String, AutoScalingConfig.TriggerListenerConfig> configs = autoScalingConfig.getTriggerListenerConfigs();
+        for (Map.Entry<String, AutoScalingConfig.TriggerListenerConfig> entry : configs.entrySet()) {
+          AutoScalingConfig.TriggerListenerConfig config = entry.getValue();
+          if (!triggerNames.contains(config.trigger)) {
+            log.debug("-- skipping listener for non-existent trigger: {}", config);
+            continue;
+          }
+          String clazz = config.listenerClass;
+          AutoScaling.TriggerListener listener = null;
+          try {
+            listener = coreContainer.getResourceLoader().newInstance(clazz, AutoScaling.TriggerListener.class);
+          } catch (Exception e) {
+            log.warn("Invalid TriggerListener class name '" + clazz + "', skipping...", e);
+          }
+          if (listener == null) {
+            continue;
+          }
+          listener.init(coreContainer, config);
+          listeners.add(listener);
+          // add per stage
+          Map<AutoScaling.EventProcessorStage, List<AutoScaling.TriggerListener>> perStage = listenerPerStage.get(config.trigger);
+          if (perStage == null) {
+            perStage = new HashMap<>();
+            listenerPerStage.put(config.trigger, perStage);
+          }
+          for (AutoScaling.EventProcessorStage stage : config.stages) {
+            List<AutoScaling.TriggerListener> lst = perStage.get(stage);
+            if (lst == null) {
+              lst = new ArrayList<>(3);
+              perStage.put(stage, lst);
+            }
+            lst.add(listener);
+          }
+        }
+      } finally {
+        updateLock.unlock();
+      }
+    }
+
+    void close() {
+      updateLock.lock();
+      try {
+        listenerPerStage.clear();
+        for (AutoScaling.TriggerListener listener : listeners) {
+          IOUtils.closeQuietly(listener);
+        }
+      } finally {
+        updateLock.unlock();
+      }
+    }
+
+    List<AutoScaling.TriggerListener> getTriggerListeners(String trigger, AutoScaling.EventProcessorStage stage) {
+      Map<AutoScaling.EventProcessorStage, List<AutoScaling.TriggerListener>> perStage = listenerPerStage.get(trigger);
+      if (perStage == null) {
+        return Collections.emptyList();
+      }
+      List<AutoScaling.TriggerListener> lst = perStage.get(stage);
+      if (lst == null) {
+        return Collections.emptyList();
+      } else {
+        return Collections.unmodifiableList(lst);
+      }
+    }
+
+    void fireListeners(String trigger, AutoScaling.EventProcessorStage stage, String actionName, TriggerEvent event, String message) {
+      updateLock.lock();
+      try {
+        for (AutoScaling.TriggerListener listener : getTriggerListeners(trigger, stage)) {
+          if (actionName != null) {
+            AutoScalingConfig.TriggerListenerConfig config = listener.getTriggerListenerConfig();
+            if (stage == AutoScaling.EventProcessorStage.BEFORE_ACTION) {
+              if (!config.beforeActions.contains(actionName)) {
+                continue;
+              }
+            } else if (stage == AutoScaling.EventProcessorStage.AFTER_ACTION) {
+              if (!config.afterActions.contains(actionName)) {
+                continue;
+              }
+            }
+          }
+          listener.onEvent(stage, actionName, event, message);
+        }
+      } finally {
+        updateLock.unlock();
+      }
+    }
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/f17f3e6a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
index be7fb0c..59170fa 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
@@ -356,7 +356,7 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
         "'trigger' : 'node_lost_trigger'," +
         "'stage' : ['STARTED','ABORTED','SUCCEEDED']," +
         "'beforeAction' : 'execute_plan'," +
-        "'class' : 'org.apache.solr.cloud.autoscaling.AutoScaling$HttpTriggerListener'," +
+        "'class' : 'org.apache.solr.cloud.autoscaling.HttpTriggerListener'," +
         "'url' : 'http://xyz.com/on_node_lost?node={$LOST_NODE_NAME}'" +
         "}" +
         "}";
@@ -371,7 +371,7 @@ public class AutoScalingHandlerTest extends SolrCloudTestCase {
     assertTrue(listeners.containsKey("xyz"));
     Map<String, Object> xyzListener = (Map<String, Object>) listeners.get("xyz");
     assertEquals(5, xyzListener.size());
-    assertEquals("org.apache.solr.cloud.autoscaling.AutoScaling$HttpTriggerListener", xyzListener.get("class").toString());
+    assertEquals("org.apache.solr.cloud.autoscaling.HttpTriggerListener", xyzListener.get("class").toString());
 
     String removeTriggerCommand = "{" +
         "'remove-trigger' : {" +


[4/5] lucene-solr:jira/solr-10996: Merge branch 'feature/autoscaling' into jira/solr-10996

Posted by ab...@apache.org.
Merge branch 'feature/autoscaling' into jira/solr-10996


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/b0eed4ce
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/b0eed4ce
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/b0eed4ce

Branch: refs/heads/jira/solr-10996
Commit: b0eed4ce3572331b02568965399a29ff688bb224
Parents: f17f3e6 fd66cb3
Author: Andrzej Bialecki <ab...@apache.org>
Authored: Wed Jul 5 15:52:50 2017 +0200
Committer: Andrzej Bialecki <ab...@apache.org>
Committed: Wed Jul 5 15:52:50 2017 +0200

----------------------------------------------------------------------
 README.md                                       |  103 +
 README.txt                                      |   13 -
 .../lucene/classification/classification.iml    |    1 -
 dev-tools/scripts/smokeTestRelease.py           |   12 +-
 lucene/CHANGES.txt                              |   12 +
 .../analysis/custom/TestCustomAnalyzer.java     |    8 +-
 .../lucene50/Lucene50SegmentInfoFormat.java     |   90 -
 .../apache/lucene/codecs/lucene50/package.html  |   25 -
 .../codecs/lucene53/Lucene53NormsFormat.java    |   91 -
 .../codecs/lucene53/Lucene53NormsProducer.java  |  236 --
 .../lucene/codecs/lucene53/package-info.java    |   23 -
 .../lucene54/Lucene54DocValuesConsumer.java     |  797 -----
 .../lucene54/Lucene54DocValuesFormat.java       |  186 -
 .../lucene54/Lucene54DocValuesProducer.java     | 1803 ----------
 .../lucene/codecs/lucene54/package-info.java    |  403 ---
 .../lucene/codecs/lucene60/Lucene60Codec.java   |  177 -
 .../apache/lucene/codecs/lucene60/package.html  |   25 -
 .../lucene/codecs/lucene62/Lucene62Codec.java   |  176 -
 .../apache/lucene/codecs/lucene62/package.html  |   25 -
 .../apache/lucene/index/FixBrokenOffsets.java   |  138 -
 .../java/org/apache/lucene/index/package.html   |   27 -
 .../services/org.apache.lucene.codecs.Codec     |    2 -
 .../org.apache.lucene.codecs.DocValuesFormat    |    1 -
 .../lucene50/Lucene50RWSegmentInfoFormat.java   |  125 -
 .../lucene50/TestLucene50SegmentInfoFormat.java |   50 -
 .../codecs/lucene53/Lucene53NormsConsumer.java  |  159 -
 .../codecs/lucene53/Lucene53RWNormsFormat.java  |   31 -
 .../lucene53/TestLucene53NormsFormat.java       |   44 -
 .../lucene54/TestLucene54DocValuesFormat.java   |  640 ----
 .../lucene/codecs/lucene60/Lucene60RWCodec.java |   38 -
 .../lucene/codecs/lucene62/Lucene62RWCodec.java |   44 -
 .../lucene62/Lucene62RWSegmentInfoFormat.java   |  193 --
 .../lucene62/TestLucene62SegmentInfoFormat.java |   48 -
 .../index/TestBackwardsCompatibility.java       |   64 +-
 .../lucene/index/TestFixBrokenOffsets.java      |  112 -
 .../lucene/index/TestIndexWriterOnOldIndex.java |    7 +-
 .../lucene/index/TestManyPointsInOldIndex.java  |    1 +
 .../org/apache/lucene/index/dvupdates.6.0.0.zip |  Bin 3420 -> 0 bytes
 .../org/apache/lucene/index/empty.6.0.0.zip     |  Bin 225 -> 0 bytes
 .../org/apache/lucene/index/index.6.0.0-cfs.zip |  Bin 15807 -> 0 bytes
 .../apache/lucene/index/index.6.0.0-nocfs.zip   |  Bin 15806 -> 0 bytes
 .../org/apache/lucene/index/index.6.0.1-cfs.zip |  Bin 15820 -> 0 bytes
 .../apache/lucene/index/index.6.0.1-nocfs.zip   |  Bin 15823 -> 0 bytes
 .../org/apache/lucene/index/index.6.1.0-cfs.zip |  Bin 15803 -> 0 bytes
 .../apache/lucene/index/index.6.1.0-nocfs.zip   |  Bin 15829 -> 0 bytes
 .../org/apache/lucene/index/index.6.2.0-cfs.zip |  Bin 15880 -> 0 bytes
 .../apache/lucene/index/index.6.2.0-nocfs.zip   |  Bin 15867 -> 0 bytes
 .../org/apache/lucene/index/index.6.2.1-cfs.zip |  Bin 15851 -> 0 bytes
 .../apache/lucene/index/index.6.2.1-nocfs.zip   |  Bin 15845 -> 0 bytes
 .../org/apache/lucene/index/index.6.3.0-cfs.zip |  Bin 15869 -> 0 bytes
 .../apache/lucene/index/index.6.3.0-nocfs.zip   |  Bin 15876 -> 0 bytes
 .../org/apache/lucene/index/index.6.4.0-cfs.zip |  Bin 15829 -> 0 bytes
 .../apache/lucene/index/index.6.4.0-nocfs.zip   |  Bin 15831 -> 0 bytes
 .../org/apache/lucene/index/index.6.4.1-cfs.zip |  Bin 15848 -> 0 bytes
 .../apache/lucene/index/index.6.4.1-nocfs.zip   |  Bin 15838 -> 0 bytes
 .../org/apache/lucene/index/index.6.4.2-cfs.zip |  Bin 15856 -> 0 bytes
 .../apache/lucene/index/index.6.4.2-nocfs.zip   |  Bin 15886 -> 0 bytes
 .../org/apache/lucene/index/index.6.5.0-cfs.zip |  Bin 15832 -> 0 bytes
 .../apache/lucene/index/index.6.5.0-nocfs.zip   |  Bin 15836 -> 0 bytes
 .../org/apache/lucene/index/index.6.5.1-cfs.zip |  Bin 15842 -> 0 bytes
 .../apache/lucene/index/index.6.5.1-nocfs.zip   |  Bin 15827 -> 0 bytes
 .../org/apache/lucene/index/index.6.6.0-cfs.zip |  Bin 15875 -> 0 bytes
 .../apache/lucene/index/index.6.6.0-nocfs.zip   |  Bin 15883 -> 0 bytes
 .../lucene/index/index.630.brokenoffsets.zip    |  Bin 3203 -> 0 bytes
 .../lucene/index/index.single-empty-doc.630.zip |  Bin 1363 -> 0 bytes
 .../org/apache/lucene/index/manypointsindex.zip |  Bin 3739 -> 0 bytes
 .../org/apache/lucene/index/maxposindex.zip     |  Bin 1702 -> 0 bytes
 .../org/apache/lucene/index/moreterms.6.0.0.zip |  Bin 157215 -> 0 bytes
 .../org/apache/lucene/index/sorted.6.2.0.zip    |  Bin 157007 -> 0 bytes
 .../org/apache/lucene/index/sorted.6.2.1.zip    |  Bin 100974 -> 0 bytes
 .../org/apache/lucene/index/sorted.6.3.0.zip    |  Bin 71090 -> 0 bytes
 .../lucene/index/unsupported.6.0.0-cfs.zip      |  Bin 0 -> 15807 bytes
 .../lucene/index/unsupported.6.0.0-nocfs.zip    |  Bin 0 -> 15806 bytes
 .../lucene/index/unsupported.6.0.1-cfs.zip      |  Bin 0 -> 15820 bytes
 .../lucene/index/unsupported.6.0.1-nocfs.zip    |  Bin 0 -> 15823 bytes
 .../lucene/index/unsupported.6.1.0-cfs.zip      |  Bin 0 -> 15803 bytes
 .../lucene/index/unsupported.6.1.0-nocfs.zip    |  Bin 0 -> 15829 bytes
 .../lucene/index/unsupported.6.2.0-cfs.zip      |  Bin 0 -> 15880 bytes
 .../lucene/index/unsupported.6.2.0-nocfs.zip    |  Bin 0 -> 15867 bytes
 .../lucene/index/unsupported.6.2.1-cfs.zip      |  Bin 0 -> 15851 bytes
 .../lucene/index/unsupported.6.2.1-nocfs.zip    |  Bin 0 -> 15845 bytes
 .../lucene/index/unsupported.6.3.0-cfs.zip      |  Bin 0 -> 15869 bytes
 .../lucene/index/unsupported.6.3.0-nocfs.zip    |  Bin 0 -> 15876 bytes
 .../lucene/index/unsupported.6.4.0-cfs.zip      |  Bin 0 -> 15829 bytes
 .../lucene/index/unsupported.6.4.0-nocfs.zip    |  Bin 0 -> 15831 bytes
 .../lucene/index/unsupported.6.4.1-cfs.zip      |  Bin 0 -> 15848 bytes
 .../lucene/index/unsupported.6.4.1-nocfs.zip    |  Bin 0 -> 15838 bytes
 .../lucene/index/unsupported.6.4.2-cfs.zip      |  Bin 0 -> 15856 bytes
 .../lucene/index/unsupported.6.4.2-nocfs.zip    |  Bin 0 -> 15886 bytes
 .../lucene/index/unsupported.6.5.0-cfs.zip      |  Bin 0 -> 15832 bytes
 .../lucene/index/unsupported.6.5.0-nocfs.zip    |  Bin 0 -> 15836 bytes
 .../lucene/index/unsupported.6.5.1-cfs.zip      |  Bin 0 -> 15842 bytes
 .../lucene/index/unsupported.6.5.1-nocfs.zip    |  Bin 0 -> 15827 bytes
 .../lucene/index/unsupported.6.6.0-cfs.zip      |  Bin 0 -> 15875 bytes
 .../lucene/index/unsupported.6.6.0-nocfs.zip    |  Bin 0 -> 15883 bytes
 lucene/classification/build.xml                 |    6 +-
 .../classification/KNearestFuzzyClassifier.java |   29 +-
 .../classification/utils/NearestFuzzyQuery.java |  333 ++
 .../KNearestFuzzyClassifierTest.java            |    2 +-
 .../lucene/document/NumericDocValuesField.java  |    6 +-
 .../document/SortedNumericDocValuesField.java   |    6 +-
 .../lucene/index/BufferedUpdatesStream.java     |    3 +
 .../org/apache/lucene/index/CheckIndex.java     |   47 +-
 .../apache/lucene/index/DocumentsWriter.java    |   20 +-
 .../lucene/index/DocumentsWriterFlushQueue.java |    4 +
 .../lucene/index/FrozenBufferedUpdates.java     |   25 +-
 .../index/IndexFormatTooOldException.java       |    2 +-
 .../org/apache/lucene/index/IndexWriter.java    |   13 +-
 .../apache/lucene/index/ReadersAndUpdates.java  |    8 +-
 .../org/apache/lucene/index/SegmentInfos.java   |    2 +-
 .../search/similarities/BM25Similarity.java     |   38 +-
 .../search/similarities/SimilarityBase.java     |   25 +-
 .../search/similarities/TFIDFSimilarity.java    |   25 +-
 .../java/org/apache/lucene/util/Version.java    |  101 +-
 .../lucene/index/TestIndexManyDocuments.java    |   71 +
 .../apache/lucene/index/TestSegmentInfos.java   |   14 +-
 .../lucene/search/TestDocValuesQueries.java     |   24 +-
 .../search/TestIndexOrDocValuesQuery.java       |    4 +-
 .../search/similarities/TestBM25Similarity.java |   42 -
 .../similarities/TestClassicSimilarity.java     |   59 +-
 .../search/similarities/TestSimilarityBase.java |   39 -
 .../org/apache/lucene/util/TestVersion.java     |   71 +-
 .../lucene/search/join/BlockJoinSelector.java   |  328 +-
 .../lucene/search/join/ToParentDocValues.java   |  296 ++
 .../search/join/TestBlockJoinSelector.java      |   76 +-
 .../apache/lucene/index/memory/MemoryIndex.java |    6 +-
 .../lucene/index/memory/TestMemoryIndex.java    |   40 +
 .../lucene/document/LatLonDocValuesField.java   |    4 +-
 .../search/TestLatLonDocValuesQueries.java      |    4 +-
 .../java/org/apache/lucene/util/TestUtil.java   |    4 +-
 lucene/version.properties                       |    2 +-
 solr/CHANGES.txt                                |  117 +
 solr/bin/solr                                   |   12 +-
 solr/bin/solr.cmd                               |    4 +-
 solr/common-build.xml                           |    5 +-
 .../collection1/conf/solrconfig-icucollate.xml  |    2 +-
 .../apache/solr/analytics/AnalyticsDriver.java  |   82 +
 .../solr/analytics/AnalyticsExpression.java     |   64 +
 .../analytics/AnalyticsGroupingManager.java     |  239 ++
 .../solr/analytics/AnalyticsRequestManager.java |  279 ++
 .../solr/analytics/AnalyticsRequestParser.java  |  549 +++
 .../solr/analytics/ExpressionFactory.java       |  821 +++++
 .../analytics/accumulator/BasicAccumulator.java |  173 -
 .../accumulator/FacetingAccumulator.java        |  730 ----
 .../analytics/accumulator/ValueAccumulator.java |   40 -
 .../facet/FacetValueAccumulator.java            |   35 -
 .../facet/FieldFacetAccumulator.java            |  153 -
 .../facet/QueryFacetAccumulator.java            |   72 -
 .../facet/RangeFacetAccumulator.java            |   49 -
 .../accumulator/facet/package-info.java         |   24 -
 .../analytics/accumulator/package-info.java     |   23 -
 .../analytics/expression/BaseExpression.java    |   88 -
 .../expression/DualDelegateExpression.java      |   99 -
 .../solr/analytics/expression/Expression.java   |   39 -
 .../analytics/expression/ExpressionFactory.java |  175 -
 .../expression/MultiDelegateExpression.java     |  131 -
 .../expression/SingleDelegateExpression.java    |   88 -
 .../solr/analytics/expression/package-info.java |   23 -
 .../analytics/facet/AbstractSolrQueryFacet.java |  104 +
 .../solr/analytics/facet/AnalyticsFacet.java    |  166 +
 .../apache/solr/analytics/facet/PivotFacet.java |  114 +
 .../apache/solr/analytics/facet/PivotNode.java  |  263 ++
 .../apache/solr/analytics/facet/QueryFacet.java |   64 +
 .../apache/solr/analytics/facet/RangeFacet.java |  119 +
 .../solr/analytics/facet/SortableFacet.java     |  178 +
 .../solr/analytics/facet/StreamingFacet.java    |   32 +
 .../apache/solr/analytics/facet/ValueFacet.java |   60 +
 .../facet/compare/ConstantComparator.java       |   30 +
 .../facet/compare/DelegatingComparator.java     |   62 +
 .../facet/compare/ExpressionComparator.java     |   46 +
 .../facet/compare/FacetResultsComparator.java   |   52 +
 .../facet/compare/FacetValueComparator.java     |   37 +
 .../analytics/facet/compare/package-info.java   |   23 +
 .../solr/analytics/facet/package-info.java      |   23 +
 .../function/ExpressionCalculator.java          |   71 +
 .../MergingReductionCollectionManager.java      |   46 +
 .../function/ReductionCollectionManager.java    |  320 ++
 .../analytics/function/ReductionFunction.java   |   37 +
 .../function/field/AnalyticsField.java          |   69 +
 .../analytics/function/field/BooleanField.java  |  111 +
 .../function/field/BooleanMultiField.java       |  101 +
 .../analytics/function/field/DateField.java     |  108 +
 .../function/field/DateMultiField.java          |   47 +
 .../function/field/DateMultiPointField.java     |   47 +
 .../analytics/function/field/DoubleField.java   |   97 +
 .../function/field/DoubleMultiField.java        |   85 +
 .../function/field/DoubleMultiPointField.java   |   81 +
 .../analytics/function/field/FloatField.java    |  108 +
 .../function/field/FloatMultiField.java         |   91 +
 .../function/field/FloatMultiPointField.java    |   87 +
 .../solr/analytics/function/field/IntField.java |  129 +
 .../analytics/function/field/IntMultiField.java |  100 +
 .../function/field/IntMultiPointField.java      |   96 +
 .../analytics/function/field/LongField.java     |  107 +
 .../function/field/LongMultiField.java          |   89 +
 .../function/field/LongMultiPointField.java     |   86 +
 .../analytics/function/field/StringField.java   |   85 +
 .../function/field/StringMultiField.java        |   66 +
 .../analytics/function/field/package-info.java  |   23 +
 .../function/mapping/AbsoluteValueFunction.java |   54 +
 .../analytics/function/mapping/AddFunction.java |   68 +
 .../function/mapping/BottomFunction.java        |  163 +
 .../function/mapping/CompareFunction.java       |  614 ++++
 .../function/mapping/ConcatFunction.java        |   78 +
 .../function/mapping/DateMathFunction.java      |  156 +
 .../function/mapping/DateParseFunction.java     |  210 ++
 .../function/mapping/DivideFunction.java        |   51 +
 .../function/mapping/FillMissingFunction.java   |  842 +++++
 .../function/mapping/FilterFunction.java        |  722 ++++
 .../analytics/function/mapping/IfFunction.java  |  892 +++++
 .../function/mapping/JoinFunction.java          |   57 +
 .../function/mapping/LambdaFunction.java        | 3220 ++++++++++++++++++
 .../analytics/function/mapping/LogFunction.java |   51 +
 .../function/mapping/LogicFunction.java         |   90 +
 .../function/mapping/MultFunction.java          |   68 +
 .../function/mapping/NegateFunction.java        |   58 +
 .../mapping/NumericConvertFunction.java         |  256 ++
 .../function/mapping/PowerFunction.java         |   51 +
 .../function/mapping/RemoveFunction.java        |  796 +++++
 .../function/mapping/ReplaceFunction.java       |  914 +++++
 .../function/mapping/StringCastFunction.java    |   42 +
 .../function/mapping/SubtractFunction.java      |   51 +
 .../analytics/function/mapping/TopFunction.java |  163 +
 .../function/mapping/package-info.java          |   23 +
 .../solr/analytics/function/package-info.java   |   23 +
 .../function/reduction/CountFunction.java       |   87 +
 .../function/reduction/DocCountFunction.java    |   87 +
 .../function/reduction/MaxFunction.java         |  298 ++
 .../function/reduction/MedianFunction.java      |  200 ++
 .../function/reduction/MinFunction.java         |  298 ++
 .../function/reduction/MissingFunction.java     |   76 +
 .../function/reduction/OrdinalFunction.java     |  386 +++
 .../function/reduction/PercentileFunction.java  |  337 ++
 .../function/reduction/SumFunction.java         |   92 +
 .../function/reduction/UniqueFunction.java      |  101 +
 .../function/reduction/data/CountCollector.java |  188 +
 .../function/reduction/data/MaxCollector.java   |  476 +++
 .../function/reduction/data/MinCollector.java   |  476 +++
 .../function/reduction/data/ReductionData.java  |   24 +
 .../reduction/data/ReductionDataCollector.java  |  183 +
 .../reduction/data/SortedListCollector.java     |  363 ++
 .../function/reduction/data/SumCollector.java   |  124 +
 .../reduction/data/UniqueCollector.java         |  241 ++
 .../function/reduction/data/package-info.java   |   24 +
 .../function/reduction/package-info.java        |   23 +
 .../org/apache/solr/analytics/package-info.java |   23 +
 .../request/AbstractFieldFacetRequest.java      |   42 -
 .../request/AnalyticsContentHandler.java        |  314 --
 .../analytics/request/AnalyticsRequest.java     |  114 -
 .../request/AnalyticsRequestFactory.java        |  308 --
 .../solr/analytics/request/AnalyticsStats.java  |  138 -
 .../analytics/request/ExpressionRequest.java    |   72 -
 .../solr/analytics/request/FacetRequest.java    |   26 -
 .../analytics/request/FieldFacetRequest.java    |  172 -
 .../analytics/request/QueryFacetRequest.java    |   74 -
 .../analytics/request/RangeFacetRequest.java    |  129 -
 .../solr/analytics/request/package-info.java    |   24 -
 .../AbstractDelegatingStatsCollector.java       |   74 -
 .../statistics/MedianStatsCollector.java        |   76 -
 .../statistics/MinMaxStatsCollector.java        |  114 -
 .../statistics/NumericStatsCollector.java       |   68 -
 .../statistics/PercentileStatsCollector.java    |   80 -
 .../analytics/statistics/StatsCollector.java    |   69 -
 .../StatsCollectorSupplierFactory.java          |  646 ----
 .../statistics/UniqueStatsCollector.java        |   53 -
 .../solr/analytics/statistics/package-info.java |   24 -
 .../stream/AnalyticsShardRequestManager.java    |  245 ++
 .../stream/AnalyticsShardResponseParser.java    |   89 +
 .../solr/analytics/stream/package-info.java     |   23 +
 .../reservation/BooleanArrayReservation.java    |   44 +
 .../reservation/BooleanCheckedReservation.java  |   42 +
 .../stream/reservation/BooleanReservation.java  |   42 +
 .../reservation/DoubleArrayReservation.java     |   44 +
 .../reservation/DoubleCheckedReservation.java   |   43 +
 .../stream/reservation/DoubleReservation.java   |   42 +
 .../reservation/FloatArrayReservation.java      |   44 +
 .../reservation/FloatCheckedReservation.java    |   43 +
 .../stream/reservation/FloatReservation.java    |   42 +
 .../stream/reservation/IntArrayReservation.java |   42 +
 .../reservation/IntCheckedReservation.java      |   43 +
 .../stream/reservation/IntReservation.java      |   42 +
 .../reservation/LongArrayReservation.java       |   45 +
 .../reservation/LongCheckedReservation.java     |   43 +
 .../stream/reservation/LongReservation.java     |   42 +
 .../ReductionCheckedDataReservation.java        |   35 +
 .../ReductionDataArrayReservation.java          |   36 +
 .../reservation/ReductionDataReservation.java   |   53 +
 .../reservation/StringArrayReservation.java     |   45 +
 .../reservation/StringCheckedReservation.java   |   44 +
 .../stream/reservation/StringReservation.java   |   43 +
 .../stream/reservation/package-info.java        |   24 +
 .../read/BooleanCheckedDataReader.java          |   33 +
 .../read/BooleanDataArrayReader.java            |   36 +
 .../reservation/read/BooleanDataReader.java     |   33 +
 .../read/DoubleCheckedDataReader.java           |   32 +
 .../reservation/read/DoubleDataArrayReader.java |   35 +
 .../reservation/read/DoubleDataReader.java      |   32 +
 .../read/FloatCheckedDataReader.java            |   33 +
 .../reservation/read/FloatDataArrayReader.java  |   36 +
 .../reservation/read/FloatDataReader.java       |   33 +
 .../reservation/read/IntCheckedDataReader.java  |   32 +
 .../reservation/read/IntDataArrayReader.java    |   34 +
 .../stream/reservation/read/IntDataReader.java  |   32 +
 .../reservation/read/LongCheckedDataReader.java |   32 +
 .../reservation/read/LongDataArrayReader.java   |   35 +
 .../stream/reservation/read/LongDataReader.java |   32 +
 .../read/ReductionCheckedDataReader.java        |   54 +
 .../read/ReductionDataArrayReader.java          |   54 +
 .../reservation/read/ReductionDataReader.java   |   40 +
 .../read/StringCheckedDataReader.java           |   32 +
 .../reservation/read/StringDataArrayReader.java |   35 +
 .../reservation/read/StringDataReader.java      |   34 +
 .../stream/reservation/read/package-info.java   |   24 +
 .../write/BooleanCheckedDataWriter.java         |   33 +
 .../write/BooleanDataArrayWriter.java           |   36 +
 .../reservation/write/BooleanDataWriter.java    |   33 +
 .../write/DoubleCheckedDataWriter.java          |   34 +
 .../write/DoubleDataArrayWriter.java            |   36 +
 .../reservation/write/DoubleDataWriter.java     |   33 +
 .../write/FloatCheckedDataWriter.java           |   35 +
 .../reservation/write/FloatDataArrayWriter.java |   37 +
 .../reservation/write/FloatDataWriter.java      |   34 +
 .../reservation/write/IntCheckedDataWriter.java |   34 +
 .../reservation/write/IntDataArrayWriter.java   |   35 +
 .../stream/reservation/write/IntDataWriter.java |   33 +
 .../write/LongCheckedDataWriter.java            |   34 +
 .../reservation/write/LongDataArrayWriter.java  |   36 +
 .../reservation/write/LongDataWriter.java       |   33 +
 .../write/ReductionCheckedDataWriter.java       |   60 +
 .../write/ReductionDataArrayWriter.java         |   53 +
 .../reservation/write/ReductionDataWriter.java  |   40 +
 .../write/StringCheckedDataWriter.java          |   34 +
 .../write/StringDataArrayWriter.java            |   36 +
 .../reservation/write/StringDataWriter.java     |   37 +
 .../stream/reservation/write/package-info.java  |   24 +
 .../solr/analytics/util/AnalyticsParams.java    |  114 -
 .../solr/analytics/util/AnalyticsParsers.java   |  171 -
 .../util/AnalyticsResponseHeadings.java         |   36 +
 .../analytics/util/FacetRangeGenerator.java     |  356 ++
 .../solr/analytics/util/MedianCalculator.java   |    4 +
 .../solr/analytics/util/OldAnalyticsParams.java |  177 +
 .../util/OldAnalyticsRequestConverter.java      |  177 +
 .../solr/analytics/util/OrdinalCalculator.java  |  173 +
 .../analytics/util/PercentileCalculator.java    |  176 -
 .../analytics/util/RangeEndpointCalculator.java |  354 --
 .../util/function/BooleanConsumer.java          |   59 +
 .../analytics/util/function/FloatConsumer.java  |   59 +
 .../analytics/util/function/FloatSupplier.java  |   41 +
 .../analytics/util/function/package-info.java   |   23 +
 .../solr/analytics/util/package-info.java       |    3 +-
 .../AbsoluteValueDoubleFunction.java            |   60 -
 .../util/valuesource/AddDoubleFunction.java     |   49 -
 .../util/valuesource/ConstDateSource.java       |  112 -
 .../util/valuesource/ConstDoubleSource.java     |  104 -
 .../util/valuesource/ConstStringSource.java     |   50 -
 .../util/valuesource/DateFieldSource.java       |  131 -
 .../util/valuesource/DateMathFunction.java      |   71 -
 .../util/valuesource/DivDoubleFunction.java     |   48 -
 .../util/valuesource/DualDoubleFunction.java    |   94 -
 .../util/valuesource/FilterFieldSource.java     |  154 -
 .../util/valuesource/LogDoubleFunction.java     |   43 -
 .../util/valuesource/MultiDateFunction.java     |  133 -
 .../util/valuesource/MultiDoubleFunction.java   |  119 -
 .../valuesource/MultiplyDoubleFunction.java     |   49 -
 .../util/valuesource/NegateDoubleFunction.java  |   55 -
 .../util/valuesource/PowDoubleFunction.java     |   48 -
 .../util/valuesource/ReverseStringFunction.java |   45 -
 .../util/valuesource/SingleDoubleFunction.java  |   79 -
 .../util/valuesource/SingleStringFunction.java  |  117 -
 .../util/valuesource/package-info.java          |   24 -
 .../solr/analytics/value/AnalyticsValue.java    |   55 +
 .../analytics/value/AnalyticsValueStream.java   |  133 +
 .../solr/analytics/value/BooleanValue.java      |   85 +
 .../analytics/value/BooleanValueStream.java     |   55 +
 .../solr/analytics/value/ComparableValue.java   |   32 +
 .../apache/solr/analytics/value/DateValue.java  |  102 +
 .../solr/analytics/value/DateValueStream.java   |   62 +
 .../solr/analytics/value/DoubleValue.java       |   86 +
 .../solr/analytics/value/DoubleValueStream.java |   54 +
 .../apache/solr/analytics/value/FloatValue.java |   97 +
 .../solr/analytics/value/FloatValueStream.java  |   60 +
 .../apache/solr/analytics/value/IntValue.java   |  121 +
 .../solr/analytics/value/IntValueStream.java    |   71 +
 .../apache/solr/analytics/value/LongValue.java  |   97 +
 .../solr/analytics/value/LongValueStream.java   |   60 +
 .../solr/analytics/value/StringValue.java       |   71 +
 .../solr/analytics/value/StringValueStream.java |   49 +
 .../value/constant/ConstantBooleanValue.java    |   91 +
 .../value/constant/ConstantDateValue.java       |  103 +
 .../value/constant/ConstantDoubleValue.java     |   90 +
 .../value/constant/ConstantFloatValue.java      |   99 +
 .../value/constant/ConstantIntValue.java        |  118 +
 .../value/constant/ConstantLongValue.java       |  100 +
 .../value/constant/ConstantStringValue.java     |   79 +
 .../analytics/value/constant/ConstantValue.java |  128 +
 .../analytics/value/constant/package-info.java  |   23 +
 .../solr/analytics/value/package-info.java      |   23 +
 .../apache/solr/handler/AnalyticsHandler.java   |  147 +
 .../handler/component/AnalyticsComponent.java   |  119 +-
 .../java/org/apache/solr/handler/package.html   |   28 +
 .../response/AnalyticsShardResponseWriter.java  |   91 +
 .../java/org/apache/solr/response/package.html  |   28 +
 .../analytics/requestFiles/expressions.txt      |   70 -
 .../analytics/requestFiles/fieldFacetExtras.txt |   66 -
 .../analytics/requestFiles/fieldFacets.txt      |  132 -
 .../analytics/requestFiles/functions.txt        |   62 -
 .../analytics/requestFiles/noFacets.txt         |   74 -
 .../analytics/requestFiles/queryFacets.txt      |   45 -
 .../analytics/requestFiles/rangeFacets.txt      |  170 -
 .../analytics/requestXMLFiles/expressions.xml   |  285 --
 .../requestXMLFiles/fieldFacetExtras.xml        |  101 -
 .../analytics/requestXMLFiles/fieldFacets.xml   |  496 ---
 .../analytics/requestXMLFiles/functions.xml     |  246 --
 .../analytics/requestXMLFiles/noFacets.xml      |  310 --
 .../analytics/requestXMLFiles/queryFacets.xml   |   94 -
 .../analytics/requestXMLFiles/rangeFacets.xml   |  319 --
 .../test-files/solr/analytics/expressions.txt   |   65 +
 .../test-files/solr/analytics/facetSorting.txt  |    4 +
 .../solr/analytics/fieldFacetExtras.txt         |   66 +
 .../test-files/solr/analytics/fieldFacets.txt   |  132 +
 .../src/test-files/solr/analytics/functions.txt |   57 +
 .../src/test-files/solr/analytics/noFacets.txt  |   74 +
 .../test-files/solr/analytics/queryFacets.txt   |   27 +
 .../test-files/solr/analytics/rangeFacets.txt   |  161 +
 .../solr/collection1/conf/schema-analytics.xml  |    7 +-
 .../collection1/conf/solrconfig-analytics.xml   |   42 +
 .../solr/collection1/conf/solrconfig-basic.xml  |   40 -
 .../configsets/cloud-analytics/conf/schema.xml  |   63 +
 .../cloud-analytics/conf/solrconfig.xml         |   59 +
 .../AbstractAnalyticsStatsCloudTest.java        |  187 +
 .../analytics/AbstractAnalyticsStatsTest.java   |   14 +-
 .../apache/solr/analytics/NoFacetCloudTest.java |  557 +++
 .../org/apache/solr/analytics/NoFacetTest.java  |   46 +-
 .../analytics/expression/ExpressionTest.java    |   70 +-
 .../solr/analytics/expression/FunctionTest.java |  221 ++
 .../facet/AbstractAnalyticsFacetCloudTest.java  |  284 ++
 .../facet/AbstractAnalyticsFacetTest.java       |   37 +-
 .../solr/analytics/facet/FacetSortingTest.java  |   53 +
 .../analytics/facet/FieldFacetCloudTest.java    | 1214 +++++++
 .../facet/FieldFacetExtrasCloudTest.java        |  253 ++
 .../analytics/facet/FieldFacetExtrasTest.java   |    6 +-
 .../solr/analytics/facet/FieldFacetTest.java    |   63 +-
 .../analytics/facet/QueryFacetCloudTest.java    |  159 +
 .../solr/analytics/facet/QueryFacetTest.java    |   10 +-
 .../analytics/facet/RangeFacetCloudTest.java    |  588 ++++
 .../solr/analytics/facet/RangeFacetTest.java    |   43 +-
 .../util/valuesource/FunctionTest.java          |  233 --
 .../solr/collection1/conf/solrconfig.xml        |   20 +-
 .../clustering/ClusteringComponentTest.java     |    2 +-
 .../collection1/conf/dataimport-solrconfig.xml  |   40 +-
 .../conf/contentstream-solrconfig.xml           |   42 +-
 .../conf/dataimport-nodatasource-solrconfig.xml |   42 +-
 .../collection1/conf/dataimport-solrconfig.xml  |   42 +-
 .../TestSolrEntityProcessorEndToEnd.java        |    4 +-
 .../solr/collection1/conf/solrconfig.xml        |   37 +-
 .../extraction/TestXLSXResponseWriter.java      |    2 +-
 .../conf/solrconfig-languageidentifier.xml      |    4 +-
 .../uima/solr/collection1/conf/solrconfig.xml   |   25 +-
 .../uima/uima-tokenizers-solrconfig.xml         |   15 +-
 .../client/solrj/embedded/JettySolrRunner.java  |    7 +
 .../org/apache/solr/cloud/AddReplicaCmd.java    |    2 +-
 .../src/java/org/apache/solr/cloud/Assign.java  |    1 -
 .../org/apache/solr/cloud/DeleteNodeCmd.java    |   43 +-
 .../org/apache/solr/cloud/DeleteReplicaCmd.java |    2 +-
 .../cloud/LeaderInitiatedRecoveryThread.java    |    7 +-
 .../org/apache/solr/cloud/MoveReplicaCmd.java   |   42 +-
 .../OverseerAutoReplicaFailoverThread.java      |    7 +-
 .../cloud/OverseerCollectionMessageHandler.java |   30 +-
 .../org/apache/solr/cloud/RecoveryStrategy.java |   32 +-
 .../org/apache/solr/cloud/ReplaceNodeCmd.java   |   43 +-
 .../org/apache/solr/cloud/SyncStrategy.java     |    8 +-
 .../org/apache/solr/cloud/ZkController.java     |    7 +-
 .../cloud/autoscaling/ExecutePlanAction.java    |   58 +-
 .../org/apache/solr/core/CoreContainer.java     |   56 +-
 .../java/org/apache/solr/core/SolrConfig.java   |    3 +-
 .../src/java/org/apache/solr/core/SolrCore.java |    2 +-
 .../java/org/apache/solr/core/SolrCores.java    |   91 +-
 .../apache/solr/core/SolrResourceLoader.java    |    1 -
 .../apache/solr/handler/CdcrRequestHandler.java |   12 +-
 .../solr/handler/CdcrUpdateLogSynchronizer.java |    7 +-
 .../org/apache/solr/handler/IndexFetcher.java   |   28 +-
 .../solr/handler/MoreLikeThisHandler.java       |    2 +-
 .../apache/solr/handler/RequestHandlerBase.java |    1 -
 .../solr/handler/StandardRequestHandler.java    |   19 +-
 .../solr/handler/admin/CollectionsHandler.java  |    7 +-
 .../component/HttpShardHandlerFactory.java      |    4 +-
 .../solr/handler/component/ResponseBuilder.java |    3 +
 .../metrics/FilteringSolrMetricReporter.java    |   59 +
 .../metrics/reporters/SolrGangliaReporter.java  |   34 +-
 .../metrics/reporters/SolrGraphiteReporter.java |   34 +-
 .../solr/metrics/reporters/SolrJmxReporter.java |   51 +-
 .../metrics/reporters/SolrSlf4jReporter.java    |   34 +-
 .../reporters/solr/SolrShardReporter.java       |   26 +-
 .../java/org/apache/solr/schema/EnumField.java  |    2 +-
 .../solr/schema/FieldTypePluginLoader.java      |    6 +-
 .../org/apache/solr/schema/IndexSchema.java     |    4 +-
 .../apache/solr/schema/IndexSchemaFactory.java  |    9 +-
 .../solr/schema/LatLonPointSpatialField.java    |    6 +-
 .../apache/solr/schema/NumericFieldType.java    |    4 +-
 .../org/apache/solr/search/SolrCoreParser.java  |    2 +-
 .../similarities/SchemaSimilarityFactory.java   |    4 +-
 .../org/apache/solr/servlet/BaseSolrFilter.java |    3 +
 .../apache/solr/servlet/BaseSolrServlet.java    |    3 +
 .../org/apache/solr/servlet/HttpSolrCall.java   |   13 +-
 .../org/apache/solr/util/Java9InitHack.java     |   78 +
 .../org/apache/solr/util/SolrPluginUtils.java   |   12 +-
 .../org/apache/solr/util/TestInjection.java     |    2 +-
 .../resources/SystemCollectionSolrConfig.xml    |    2 +-
 .../collection1/conf/bad-error-solrconfig.xml   |    2 +-
 .../collection1/conf/bad-mpf-solrconfig.xml     |    2 +-
 .../conf/bad-schema-dup-fieldType.xml           |    4 +-
 .../collection1/conf/schema-binaryfield.xml     |   36 -
 .../collection1/conf/schema-copyfield-test.xml  |   34 +-
 .../collection1/conf/schema-customfield.xml     |   29 +-
 .../conf/schema-distributed-missing-sort.xml    |   28 +-
 .../solr/collection1/conf/schema-docValues.xml  |   10 +-
 .../conf/schema-docValuesFaceting.xml           |   10 +-
 .../collection1/conf/schema-docValuesJoin.xml   |   10 +-
 .../conf/schema-docValuesMissing.xml            |   10 +-
 .../collection1/conf/schema-docValuesMulti.xml  |   10 +-
 .../conf/schema-field-sort-values.xml           |    4 +-
 .../conf/schema-non-stored-docvalues.xml        |   47 +-
 .../conf/schema-not-required-unique-key.xml     |    1 -
 .../collection1/conf/schema-replication1.xml    |    1 -
 .../collection1/conf/schema-replication2.xml    |    1 -
 .../collection1/conf/schema-required-fields.xml |   34 +-
 .../solr/collection1/conf/schema-reversed.xml   |    5 +-
 .../collection1/conf/schema-sortingresponse.xml |   22 +-
 .../collection1/conf/schema-tokenizer-test.xml  |   21 +-
 .../solr/collection1/conf/schema-version-dv.xml |    5 +-
 .../collection1/conf/schema-version-indexed.xml |    5 +-
 .../solr/collection1/conf/schema15.xml          |   22 +-
 ...dd-schema-fields-update-processor-chains.xml |    2 +-
 .../conf/solrconfig-altdirectory.xml            |    2 +-
 .../conf/solrconfig-analytics-query.xml         |  227 +-
 .../solr/collection1/conf/solrconfig-basic.xml  |    2 +-
 .../conf/solrconfig-blockjoinfacetcomponent.xml |   11 +-
 .../conf/solrconfig-cache-enable-disable.xml    |    4 +-
 .../solr/collection1/conf/solrconfig-cdcr.xml   |    2 +-
 .../conf/solrconfig-cdcrupdatelog.xml           |    2 +-
 .../conf/solrconfig-classification.xml          |    2 +-
 .../conf/solrconfig-collapseqparser.xml         |  125 +-
 .../conf/solrconfig-components-name.xml         |    4 +-
 .../solrconfig-configurerecoverystrategy.xml    |    2 +-
 .../conf/solrconfig-customrecoverystrategy.xml  |    2 +-
 .../conf/solrconfig-delaying-component.xml      |    2 +-
 .../collection1/conf/solrconfig-delpolicy1.xml  |    2 +-
 .../collection1/conf/solrconfig-delpolicy2.xml  |    2 +-
 ...lrconfig-distrib-update-processor-chains.xml |    3 +-
 .../conf/solrconfig-doctransformers.xml         |    4 +-
 .../collection1/conf/solrconfig-elevate.xml     |    2 +-
 .../solrconfig-externalversionconstraint.xml    |    2 +-
 .../conf/solrconfig-functionquery.xml           |    4 +-
 .../solr/collection1/conf/solrconfig-hash.xml   |    4 +-
 .../collection1/conf/solrconfig-headers.xml     |    2 +-
 .../collection1/conf/solrconfig-highlight.xml   |    2 +-
 .../conf/solrconfig-implicitproperties.xml      |    2 +-
 .../conf/solrconfig-infixsuggesters.xml         |    2 +-
 .../conf/solrconfig-logmergepolicyfactory.xml   |    2 +-
 .../conf/solrconfig-managed-schema-test.xml     |    2 +-
 .../conf/solrconfig-managed-schema.xml          |    4 +-
 .../conf/solrconfig-master-throttled.xml        |    8 +-
 .../solr/collection1/conf/solrconfig-master.xml |    8 +-
 .../conf/solrconfig-master1-keepOneBackup.xml   |    4 +-
 .../collection1/conf/solrconfig-master1.xml     |    8 +-
 .../collection1/conf/solrconfig-master2.xml     |    8 +-
 .../collection1/conf/solrconfig-master3.xml     |    8 +-
 .../conf/solrconfig-mergepolicy-defaults.xml    |    2 +-
 .../conf/solrconfig-mergepolicy-legacy.xml      |    2 +-
 .../solrconfig-mergepolicyfactory-nocfs.xml     |    2 +-
 .../collection1/conf/solrconfig-nocache.xml     |    8 +-
 .../conf/solrconfig-nomergepolicyfactory.xml    |    2 +-
 .../collection1/conf/solrconfig-noopregen.xml   |    2 +-
 .../collection1/conf/solrconfig-paramset.xml    |    2 +-
 ...lrconfig-parsing-update-processor-chains.xml |    2 +-
 .../conf/solrconfig-phrasesuggest.xml           |    2 +-
 .../conf/solrconfig-plugcollector.xml           |   28 +-
 .../conf/solrconfig-postingshighlight.xml       |    2 +-
 .../conf/solrconfig-query-parser-init.xml       |    2 +-
 .../conf/solrconfig-querysender-noquery.xml     |    2 +-
 .../collection1/conf/solrconfig-querysender.xml |    8 +-
 .../collection1/conf/solrconfig-repeater.xml    |    8 +-
 .../collection1/conf/solrconfig-reqHandler.incl |    2 +-
 .../conf/solrconfig-response-log-component.xml  |   16 +-
 .../collection1/conf/solrconfig-schemaless.xml  |    2 +-
 .../conf/solrconfig-script-updateprocessor.xml  |    2 +-
 .../conf/solrconfig-searcher-listeners1.xml     |    2 +-
 .../solr/collection1/conf/solrconfig-slave.xml  |    8 +-
 .../solr/collection1/conf/solrconfig-slave1.xml |    8 +-
 .../conf/solrconfig-solcoreproperties.xml       |    2 +-
 .../solrconfig-sortingmergepolicyfactory.xml    |    4 +-
 .../conf/solrconfig-sortingresponse.xml         |    2 +-
 .../collection1/conf/solrconfig-spatial.xml     |    2 +-
 .../conf/solrconfig-spellcheckcomponent.xml     |   12 +-
 .../conf/solrconfig-spellchecker.xml            |    2 +-
 .../solr/collection1/conf/solrconfig-sql.xml    |    4 +-
 ...-suggestercomponent-context-filter-query.xml |    2 +-
 .../conf/solrconfig-suggestercomponent.xml      |    2 +-
 .../collection1/conf/solrconfig-test-misc.xml   |    4 +-
 .../conf/solrconfig-testxmlparser.xml           |    2 +-
 .../solrconfig-tieredmergepolicyfactory.xml     |    2 +-
 .../solr/collection1/conf/solrconfig-tlog.xml   |    5 +-
 ...nfig-uninvertdocvaluesmergepolicyfactory.xml |    2 +-
 .../conf/solrconfig-update-processor-chains.xml |    2 +-
 ...lrconfig-warmer-randommergepolicyfactory.xml |    2 +-
 .../solr/collection1/conf/solrconfig.xml        |   35 +-
 .../conf/solrconfig_SimpleTextCodec.xml         |    2 +-
 .../solr/collection1/conf/solrconfig_codec.xml  |    2 +-
 .../solr/collection1/conf/solrconfig_codec2.xml |    2 +-
 .../solr/collection1/conf/solrconfig_perf.xml   |    5 +-
 .../solr/configsets/_default/conf/currency.xml  |   67 +
 .../solr/configsets/_default/conf/elevate.xml   |   42 +
 .../_default/conf/lang/contractions_ca.txt      |    8 +
 .../_default/conf/lang/contractions_fr.txt      |   15 +
 .../_default/conf/lang/contractions_ga.txt      |    5 +
 .../_default/conf/lang/contractions_it.txt      |   23 +
 .../_default/conf/lang/hyphenations_ga.txt      |    5 +
 .../_default/conf/lang/stemdict_nl.txt          |    6 +
 .../_default/conf/lang/stoptags_ja.txt          |  420 +++
 .../_default/conf/lang/stopwords_ar.txt         |  125 +
 .../_default/conf/lang/stopwords_bg.txt         |  193 ++
 .../_default/conf/lang/stopwords_ca.txt         |  220 ++
 .../_default/conf/lang/stopwords_cz.txt         |  172 +
 .../_default/conf/lang/stopwords_da.txt         |  110 +
 .../_default/conf/lang/stopwords_de.txt         |  294 ++
 .../_default/conf/lang/stopwords_el.txt         |   78 +
 .../_default/conf/lang/stopwords_en.txt         |   54 +
 .../_default/conf/lang/stopwords_es.txt         |  356 ++
 .../_default/conf/lang/stopwords_eu.txt         |   99 +
 .../_default/conf/lang/stopwords_fa.txt         |  313 ++
 .../_default/conf/lang/stopwords_fi.txt         |   97 +
 .../_default/conf/lang/stopwords_fr.txt         |  186 +
 .../_default/conf/lang/stopwords_ga.txt         |  110 +
 .../_default/conf/lang/stopwords_gl.txt         |  161 +
 .../_default/conf/lang/stopwords_hi.txt         |  235 ++
 .../_default/conf/lang/stopwords_hu.txt         |  211 ++
 .../_default/conf/lang/stopwords_hy.txt         |   46 +
 .../_default/conf/lang/stopwords_id.txt         |  359 ++
 .../_default/conf/lang/stopwords_it.txt         |  303 ++
 .../_default/conf/lang/stopwords_ja.txt         |  127 +
 .../_default/conf/lang/stopwords_lv.txt         |  172 +
 .../_default/conf/lang/stopwords_nl.txt         |  119 +
 .../_default/conf/lang/stopwords_no.txt         |  194 ++
 .../_default/conf/lang/stopwords_pt.txt         |  253 ++
 .../_default/conf/lang/stopwords_ro.txt         |  233 ++
 .../_default/conf/lang/stopwords_ru.txt         |  243 ++
 .../_default/conf/lang/stopwords_sv.txt         |  133 +
 .../_default/conf/lang/stopwords_th.txt         |  119 +
 .../_default/conf/lang/stopwords_tr.txt         |  212 ++
 .../_default/conf/lang/userdict_ja.txt          |   29 +
 .../configsets/_default/conf/managed-schema     | 1076 ++++++
 .../solr/configsets/_default/conf/params.json   |   20 +
 .../solr/configsets/_default/conf/protwords.txt |   21 +
 .../solr/configsets/_default/conf/schema.xml    |   37 -
 .../configsets/_default/conf/solrconfig.xml     | 1355 +++++++-
 .../solr/configsets/_default/conf/stopwords.txt |   14 +
 .../solr/configsets/_default/conf/synonyms.txt  |   29 +
 .../bad-mergepolicy/conf/solrconfig.xml         |    2 +-
 .../cdcr-source-disabled/conf/schema.xml        |    2 +-
 .../solr/configsets/cdcr-source/conf/schema.xml |    2 +-
 .../configsets/cdcr-source/conf/solrconfig.xml  |    3 +-
 .../solr/configsets/cdcr-target/conf/schema.xml |    2 +-
 .../configsets/cdcr-target/conf/solrconfig.xml  |    3 +-
 .../exitable-directory/conf/solrconfig.xml      |    2 +-
 .../test-files/solr/crazy-path-to-config.xml    |   10 +-
 solr/core/src/test-files/solr/solr.xml          |    1 +
 .../org/apache/solr/BasicFunctionalityTest.java |    6 +-
 .../org/apache/solr/ConvertedLegacyTest.java    |   46 +-
 .../apache/solr/DisMaxRequestHandlerTest.java   |    8 +-
 .../test/org/apache/solr/EchoParamsTest.java    |    4 +-
 .../src/test/org/apache/solr/SampleTest.java    |    2 +-
 .../test/org/apache/solr/SolrInfoBeanTest.java  |    4 +-
 .../org/apache/solr/TestDistributedSearch.java  |    6 +-
 .../org/apache/solr/TestRandomDVFaceting.java   |   28 +-
 .../client/solrj/impl/ConnectionReuseTest.java  |    4 +-
 .../solr/cloud/BaseCdcrDistributedZkTest.java   |    3 +-
 .../solr/cloud/BasicDistributedZkTest.java      |   51 +-
 .../test/org/apache/solr/cloud/BasicZkTest.java |    2 +-
 .../cloud/ChaosMonkeyNothingIsSafeTest.java     |   19 +-
 ...MonkeyNothingIsSafeWithPullReplicasTest.java |   20 +-
 .../solr/cloud/CollectionsAPISolrJTest.java     |   27 +-
 .../org/apache/solr/cloud/DeleteNodeTest.java   |   61 +-
 .../cloud/FullSolrCloudDistribCmdsTest.java     |    3 +-
 .../FullThrottleStoppableIndexingThread.java    |    4 +-
 .../apache/solr/cloud/HttpPartitionTest.java    |    4 +-
 .../apache/solr/cloud/MigrateRouteKeyTest.java  |    8 +-
 .../apache/solr/cloud/MoveReplicaHDFSTest.java  |   69 +
 .../org/apache/solr/cloud/MoveReplicaTest.java  |   83 +-
 .../org/apache/solr/cloud/ShardSplitTest.java   |   10 +-
 .../org/apache/solr/cloud/SyncSliceTest.java    |    5 +-
 .../apache/solr/cloud/TestCloudRecovery.java    |    8 -
 .../apache/solr/cloud/TestConfigSetsAPI.java    |   49 +
 .../org/apache/solr/cloud/TestPrepRecovery.java |  109 +
 .../cloud/TestRandomRequestDistribution.java    |   12 +-
 .../cloud/TestRequestStatusCollectionAPI.java   |    3 +-
 .../TestSolrCloudWithDelegationTokens.java      |    3 -
 .../solr/cloud/UnloadDistributedZkTest.java     |   41 +-
 .../org/apache/solr/cloud/ZkControllerTest.java |   19 +-
 .../autoscaling/AutoScalingHandlerTest.java     |   36 +-
 .../autoscaling/ComputePlanActionTest.java      |   41 +-
 .../autoscaling/ExecutePlanActionTest.java      |  183 +
 .../autoscaling/TriggerIntegrationTest.java     |   50 +-
 .../apache/solr/cloud/hdfs/StressHdfsTest.java  |    3 +-
 .../solr/core/AlternateDirectoryTest.java       |    2 +-
 .../apache/solr/core/DirectoryFactoryTest.java  |   15 +-
 .../solr/core/OpenCloseCoreStressTest.java      |    8 +-
 .../apache/solr/core/RequestHandlersTest.java   |    8 +-
 .../test/org/apache/solr/core/SolrCoreTest.java |    2 +-
 .../test/org/apache/solr/core/TestConfig.java   |    4 +-
 .../org/apache/solr/core/TestCoreDiscovery.java |    3 +-
 .../org/apache/solr/core/TestLazyCores.java     |   44 +-
 .../solr/core/TestQuerySenderListener.java      |    2 +-
 .../solr/core/TestQuerySenderNoQuery.java       |    2 +-
 .../apache/solr/core/TestXIncludeConfig.java    |    2 +-
 .../solr/handler/MoreLikeThisHandlerTest.java   |    1 -
 .../apache/solr/handler/SearchHandlerTest.java  |   80 +
 .../handler/StandardRequestHandlerTest.java     |   88 -
 .../solr/handler/TestReplicationHandler.java    |    4 +-
 .../handler/TestReplicationHandlerBackup.java   |    4 +-
 .../apache/solr/handler/TestRestoreCore.java    |    4 +-
 .../solr/handler/V2ApiIntegrationTest.java      |    9 +-
 .../handler/admin/CoreAdminHandlerTest.java     |   36 +-
 .../DistributedSpellCheckComponentTest.java     |   10 +-
 .../handler/component/FacetPivotSmallTest.java  |    1 -
 .../component/ResponseLogComponentTest.java     |   12 +-
 .../component/SpellCheckComponentTest.java      |   10 +-
 .../handler/component/StatsComponentTest.java   |    1 -
 .../handler/component/SuggestComponentTest.java |    2 +-
 .../component/TermVectorComponentTest.java      |    2 +-
 .../highlight/FastVectorHighlighterTest.java    |    2 +-
 .../solr/highlight/HighlighterConfigTest.java   |    2 +-
 .../apache/solr/highlight/HighlighterTest.java  |   84 +-
 .../metrics/SolrMetricsIntegrationTest.java     |    2 +-
 .../metrics/reporters/SolrJmxReporterTest.java  |   18 +-
 .../apache/solr/request/SimpleFacetsTest.java   |    4 +-
 .../solr/request/TestIntervalFaceting.java      |    2 +-
 .../apache/solr/schema/DocValuesMultiTest.java  |   10 +-
 .../search/TestComplexPhraseQParserPlugin.java  |    8 +-
 .../search/join/BlockJoinFacetDistribTest.java  |    2 +-
 .../search/join/BlockJoinFacetRandomTest.java   |    2 +-
 .../search/join/BlockJoinFacetSimpleTest.java   |    2 +-
 .../TestNonDefinedSimilarityFactory.java        |   10 -
 .../hadoop/TestDelegationWithHadoopAuth.java    |    1 -
 .../spelling/DirectSolrSpellCheckerTest.java    |    2 +-
 .../solr/spelling/SpellCheckCollatorTest.java   |   43 +-
 .../SpellCheckCollatorWithCollapseTest.java     |    2 +-
 .../spelling/WordBreakSolrSpellCheckerTest.java |   16 +-
 .../example-DIH/solr/atom/conf/solrconfig.xml   |    2 +-
 .../example-DIH/solr/db/conf/solrconfig.xml     |   26 +-
 .../example-DIH/solr/mail/conf/solrconfig.xml   |   26 +-
 .../example-DIH/solr/solr/conf/solrconfig.xml   |   26 +-
 .../example-DIH/solr/tika/conf/solrconfig.xml   |    2 +-
 solr/example/files/conf/solrconfig.xml          |   26 +-
 .../configsets/_default/conf/solrconfig.xml     |   25 +-
 .../conf/solrconfig.xml                         |   25 +-
 .../src/cross-data-center-replication-cdcr.adoc |    6 +-
 .../src/distributed-requests.adoc               |    4 +-
 .../src/the-dismax-query-parser.adoc            |   16 +-
 .../src/uploading-data-with-index-handlers.adoc |    2 +-
 .../solr/client/solrj/impl/CloudSolrClient.java |   67 +-
 .../solrj/impl/ConcurrentUpdateSolrClient.java  |   36 +
 .../solr/client/solrj/impl/HttpSolrClient.java  |   40 +-
 .../client/solrj/impl/LBHttpSolrClient.java     |   60 +-
 .../solr/common/params/MoreLikeThisParams.java  |    3 +-
 .../solr/collection1/conf/solrconfig-slave1.xml |    8 +-
 .../solr/collection1/conf/solrconfig-sql.xml    |    4 +-
 .../solrj/solr/collection1/conf/solrconfig.xml  |    4 +-
 .../solr/configsets/ml/conf/solrconfig.xml      |    4 +-
 .../solr/configsets/shared/conf/solrconfig.xml  |    8 +-
 .../configsets/streaming/conf/solrconfig.xml    |    6 +-
 .../solr/multicore/core0/conf/solrconfig.xml    |    8 +-
 .../solr/multicore/core1/conf/solrconfig.xml    |    8 +-
 .../client/solrj/SolrExampleBinaryTest.java     |    3 +-
 .../solr/client/solrj/SolrExampleXMLTest.java   |    3 +-
 .../solr/client/solrj/SolrExceptionTest.java    |    3 +-
 .../client/solrj/SolrSchemalessExampleTest.java |    3 +-
 .../solr/client/solrj/TestLBHttpSolrClient.java |    4 +-
 .../embedded/SolrExampleStreamingTest.java      |    2 +-
 .../solrj/impl/BasicHttpSolrClientTest.java     |    3 +-
 .../client/solrj/impl/CloudSolrClientTest.java  |    6 +-
 .../solrj/impl/HttpSolrClientConPoolTest.java   |    3 +-
 .../java/org/apache/solr/SolrJettyTestBase.java |    3 +-
 .../java/org/apache/solr/SolrTestCaseJ4.java    |  148 +-
 .../cloud/AbstractFullDistribZkTestBase.java    |   31 +-
 784 files changed, 42728 insertions(+), 18623 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b0eed4ce/solr/core/src/test/org/apache/solr/cloud/autoscaling/AutoScalingHandlerTest.java
----------------------------------------------------------------------


[5/5] lucene-solr:jira/solr-10996: SOLR-10996 Some refactoring to use base classes, plus unit tests.

Posted by ab...@apache.org.
SOLR-10996 Some refactoring to use base classes, plus unit tests.


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/b52e1148
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/b52e1148
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/b52e1148

Branch: refs/heads/jira/solr-10996
Commit: b52e114806531b96e0fc1df5fe5e57056056349c
Parents: b0eed4c
Author: Andrzej Bialecki <ab...@apache.org>
Authored: Wed Jul 5 21:42:12 2017 +0200
Committer: Andrzej Bialecki <ab...@apache.org>
Committed: Wed Jul 5 21:42:12 2017 +0200

----------------------------------------------------------------------
 .../solr/cloud/autoscaling/AutoScaling.java     |  21 --
 .../cloud/autoscaling/AutoScalingConfig.java    |  44 +++-
 .../cloud/autoscaling/AutoScalingHandler.java   |   2 +-
 .../cloud/autoscaling/ComputePlanAction.java    |  19 +-
 .../cloud/autoscaling/ExecutePlanAction.java    |  20 +-
 .../cloud/autoscaling/HttpTriggerListener.java  |  17 +-
 .../solr/cloud/autoscaling/LogPlanAction.java   |  20 +-
 .../cloud/autoscaling/LogTriggerListener.java   |  21 +-
 .../cloud/autoscaling/ScheduledTriggers.java    |  51 ++--
 .../cloud/autoscaling/TriggerActionBase.java    |  47 ++++
 .../solr/cloud/autoscaling/TriggerListener.java |  42 ++++
 .../cloud/autoscaling/TriggerListenerBase.java  |  46 ++++
 .../autoscaling/TriggerIntegrationTest.java     | 246 ++++++++++++++++---
 13 files changed, 420 insertions(+), 176 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b52e1148/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
index c1a1169..fa7311c 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScaling.java
@@ -64,27 +64,6 @@ public class AutoScaling {
   }
 
   /**
-   * Implementations of this interface are notified of stages in event processing that they were
-   * registered for. Note: instances may be closed and re-created on each auto-scaling config update.
-   */
-  public interface TriggerListener extends Closeable {
-
-    void init(CoreContainer coreContainer, AutoScalingConfig.TriggerListenerConfig config);
-
-    AutoScalingConfig.TriggerListenerConfig getTriggerListenerConfig();
-
-    /**
-     * This method is called when either a particular <code>stage</code> or
-     * <code>actionName</code> is reached during event processing.
-     * @param stage {@link EventProcessorStage} that this listener was registered for, or null
-     * @param actionName {@link TriggerAction} name that this listener was registered for, or null
-     * @param event current event being processed
-     * @param message optional message
-     */
-    void onEvent(EventProcessorStage stage, String actionName, TriggerEvent event, String message);
-  }
-
-  /**
    * Interface for a Solr trigger. Each trigger implements Runnable and Closeable interface. A trigger
    * is scheduled using a {@link java.util.concurrent.ScheduledExecutorService} so it is executed as
    * per a configured schedule to check whether the trigger is ready to fire. The {@link Trigger#setProcessor(EventProcessor)}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b52e1148/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java
index f2c251c..056091d 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingConfig.java
@@ -17,7 +17,6 @@
 package org.apache.solr.cloud.autoscaling;
 
 import java.lang.invoke.MethodHandles;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -46,9 +45,10 @@ public class AutoScalingConfig {
   private Map<String, TriggerListenerConfig> listeners;
 
   /**
-   * Bean representation of {@link org.apache.solr.cloud.autoscaling.AutoScaling.TriggerListener} config.
+   * Bean representation of {@link TriggerListener} config.
    */
   public static class TriggerListenerConfig {
+    public final String name;
     public final String trigger;
     public final Set<AutoScaling.EventProcessorStage> stages;
     public final String listenerClass;
@@ -56,22 +56,23 @@ public class AutoScalingConfig {
     public final Set<String> afterActions;
     public final Map<String, Object> properties = new HashMap<>();
 
-    public TriggerListenerConfig(Map<String, Object> properties) {
+    public TriggerListenerConfig(String name, Map<String, Object> properties) {
+      this.name = name;
       this.properties.putAll(properties);
       trigger = (String)properties.get(AutoScalingParams.TRIGGER);
-      List<String> stageNames = (List<String>)properties.getOrDefault(AutoScalingParams.STAGE, Collections.emptyList());
+      List<String> stageNames = getList(AutoScalingParams.STAGE, properties);
       stages = new HashSet<>(stageNames.size());
-      for (String name : stageNames) {
+      for (String stageName : stageNames) {
         try {
-          AutoScaling.EventProcessorStage stage = AutoScaling.EventProcessorStage.valueOf(name.toUpperCase(Locale.ROOT));
+          AutoScaling.EventProcessorStage stage = AutoScaling.EventProcessorStage.valueOf(stageName.toUpperCase(Locale.ROOT));
           stages.add(stage);
         } catch (Exception e) {
           LOG.warn("Invalid stage name '" + name + "' in listener config, skipping: " + properties);
         }
       }
       listenerClass = (String)properties.get(AutoScalingParams.CLASS);
-      beforeActions = new HashSet<>((List<String>)properties.getOrDefault(AutoScalingParams.BEFORE_ACTION, Collections.emptyList()));
-      afterActions = new HashSet<>((List<String>)properties.getOrDefault(AutoScalingParams.AFTER_ACTION, Collections.emptyList()));
+      beforeActions = new HashSet<>(getList(AutoScalingParams.BEFORE_ACTION, properties));
+      afterActions = new HashSet<>(getList(AutoScalingParams.AFTER_ACTION, properties));
     }
   }
 
@@ -79,10 +80,12 @@ public class AutoScalingConfig {
    * Bean representation of {@link org.apache.solr.cloud.autoscaling.AutoScaling.Trigger} config.
    */
   public static class TriggerConfig {
+    public final String name;
     public final AutoScaling.EventType eventType;
     public final Map<String, Object> properties = new HashMap<>();
 
-    public TriggerConfig(Map<String, Object> properties) {
+    public TriggerConfig(String name, Map<String, Object> properties) {
+      this.name = name;
       String event = (String) properties.get(AutoScalingParams.EVENT);
       this.eventType = AutoScaling.EventType.valueOf(event.toUpperCase(Locale.ROOT));
       this.properties.putAll(properties);
@@ -129,7 +132,7 @@ public class AutoScalingConfig {
       } else {
         triggers = new HashMap<>(trigMap.size());
         for (Map.Entry<String, Object> entry : trigMap.entrySet()) {
-          triggers.put(entry.getKey(), new TriggerConfig((Map<String, Object>)entry.getValue()));
+          triggers.put(entry.getKey(), new TriggerConfig(entry.getKey(), (Map<String, Object>)entry.getValue()));
         }
       }
     }
@@ -167,11 +170,30 @@ public class AutoScalingConfig {
       } else {
         listeners = new HashMap<>(map.size());
         for (Map.Entry<String, Object> entry : map.entrySet()) {
-          listeners.put(entry.getKey(), new TriggerListenerConfig((Map<String, Object>)entry.getValue()));
+          listeners.put(entry.getKey(), new TriggerListenerConfig(entry.getKey(), (Map<String, Object>)entry.getValue()));
         }
       }
     }
     return listeners;
   }
 
+  private static List<String> getList(String key, Map<String, Object> properties) {
+    return getList(key, properties, null);
+  }
+
+  private static List<String> getList(String key, Map<String, Object> properties, List<String> defaultList) {
+    if (defaultList == null) {
+      defaultList = Collections.emptyList();
+    }
+    Object o = properties.get(key);
+    if (o == null) {
+      return defaultList;
+    }
+    if (o instanceof List) {
+      return (List)o;
+    } else {
+      return Collections.singletonList(String.valueOf(o));
+    }
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b52e1148/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java
index 5f877b8..e730088 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/AutoScalingHandler.java
@@ -391,7 +391,7 @@ public class AutoScalingHandler extends RequestHandlerBase implements Permission
     // validate that we can load the listener class
     // todo nocommit -- what about MemClassLoader?
     try {
-      container.getResourceLoader().findClass(listenerClass, AutoScaling.TriggerListener.class);
+      container.getResourceLoader().findClass(listenerClass, TriggerListener.class);
     } catch (Exception e) {
       throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Listener not found: " + listenerClass, e);
     }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b52e1148/solr/core/src/java/org/apache/solr/cloud/autoscaling/ComputePlanAction.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ComputePlanAction.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/ComputePlanAction.java
index cb876a5..5cb8f1c 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ComputePlanAction.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/ComputePlanAction.java
@@ -42,26 +42,9 @@ import org.slf4j.LoggerFactory;
  * The cluster operations computed here are put into the {@link ActionContext}'s properties
  * with the key name "operations". The value is a List of SolrRequest objects.
  */
-public class ComputePlanAction implements TriggerAction {
+public class ComputePlanAction extends TriggerActionBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-  private Map<String, String> initArgs;
-
-  @Override
-  public void close() throws IOException {
-
-  }
-
-  @Override
-  public void init(Map<String, String> args) {
-    this.initArgs = args;
-  }
-
-  @Override
-  public String getName() {
-    return initArgs.get("name");
-  }
-
   @Override
   public void process(TriggerEvent event, ActionContext context) {
     log.debug("-- processing event: {} with context properties: {}", event, context.getProperties());

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b52e1148/solr/core/src/java/org/apache/solr/cloud/autoscaling/ExecutePlanAction.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ExecutePlanAction.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/ExecutePlanAction.java
index 86e24f4..2cd9824 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ExecutePlanAction.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/ExecutePlanAction.java
@@ -21,7 +21,6 @@ import java.io.IOException;
 import java.lang.invoke.MethodHandles;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.solr.client.solrj.SolrRequest;
 import org.apache.solr.client.solrj.SolrResponse;
@@ -38,26 +37,9 @@ import org.slf4j.LoggerFactory;
  * This class is responsible for executing cluster operations read from the {@link ActionContext}'s properties
  * with the key name "operations"
  */
-public class ExecutePlanAction implements TriggerAction {
+public class ExecutePlanAction extends TriggerActionBase {
   private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-  private Map<String, String> initArgs;
-
-  @Override
-  public void close() throws IOException {
-
-  }
-
-  @Override
-  public void init(Map<String, String> args) {
-    this.initArgs = args;
-  }
-
-  @Override
-  public String getName() {
-    return initArgs.get("name");
-  }
-
   @Override
   public void process(TriggerEvent event, ActionContext context) {
     log.debug("-- processing event: {} with context properties: {}", event, context.getProperties());

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b52e1148/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java
index 84cccf0..4253bf6 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/HttpTriggerListener.java
@@ -16,22 +16,19 @@
  */
 package org.apache.solr.cloud.autoscaling;
 
-import java.io.IOException;
-
 import org.apache.http.client.HttpClient;
 import org.apache.solr.core.CoreContainer;
 
 /**
  * Simple HTTP callback that sends trigger events as JSON.
  */
-public class HttpTriggerListener implements AutoScaling.TriggerListener {
+public class HttpTriggerListener extends TriggerListenerBase {
 
   private HttpClient httpClient;
-  private AutoScalingConfig.TriggerListenerConfig config;
 
   @Override
   public void init(CoreContainer coreContainer, AutoScalingConfig.TriggerListenerConfig config) {
-    this.config = config;
+    super.init(coreContainer, config);
     httpClient = coreContainer.getUpdateShardHandler().getHttpClient();
   }
 
@@ -39,14 +36,4 @@ public class HttpTriggerListener implements AutoScaling.TriggerListener {
   public void onEvent(AutoScaling.EventProcessorStage stage, String actionName, TriggerEvent event, String message) {
 
   }
-
-  @Override
-  public AutoScalingConfig.TriggerListenerConfig getTriggerListenerConfig() {
-    return config;
-  }
-
-  @Override
-  public void close() throws IOException {
-
-  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b52e1148/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogPlanAction.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogPlanAction.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogPlanAction.java
index 7b2de80..45107c1 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogPlanAction.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogPlanAction.java
@@ -17,28 +17,10 @@
 
 package org.apache.solr.cloud.autoscaling;
 
-import java.io.IOException;
-import java.util.Map;
-
 /**
  * todo nocommit
  */
-public class LogPlanAction implements TriggerAction {
-  @Override
-  public void close() throws IOException {
-
-  }
-
-  @Override
-  public void init(Map<String, String> args) {
-
-  }
-
-  @Override
-  public String getName() {
-    return null;
-  }
-
+public class LogPlanAction extends TriggerActionBase {
   @Override
   public void process(TriggerEvent event, ActionContext actionContext) {
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b52e1148/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java
index 2246b73..bf00114 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/LogTriggerListener.java
@@ -25,31 +25,14 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Implementation of {@link org.apache.solr.cloud.autoscaling.AutoScaling.TriggerListener} that reports
+ * Implementation of {@link TriggerListener} that reports
  * events to a log.
  */
-public class LogTriggerListener implements AutoScaling.TriggerListener {
+public class LogTriggerListener extends TriggerListenerBase {
   private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
 
-  private AutoScalingConfig.TriggerListenerConfig config;
-
-  @Override
-  public void init(CoreContainer coreContainer, AutoScalingConfig.TriggerListenerConfig config) {
-    this.config = config;
-  }
-
-  @Override
-  public AutoScalingConfig.TriggerListenerConfig getTriggerListenerConfig() {
-    return config;
-  }
-
   @Override
   public void onEvent(AutoScaling.EventProcessorStage stage, String actionName, TriggerEvent event, String message) {
     LOG.info("stage={}, actionName={}, event={}, messsage={}", stage, actionName, event, message);
   }
-
-  @Override
-  public void close() throws IOException {
-
-  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b52e1148/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java
index 1be62a4..d5d29de 100644
--- a/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/ScheduledTriggers.java
@@ -369,8 +369,8 @@ public class ScheduledTriggers implements Closeable {
   }
 
   private class TriggerListeners {
-    Map<String, Map<AutoScaling.EventProcessorStage, List<AutoScaling.TriggerListener>>> listenerPerStage = new HashMap<>();
-    List<AutoScaling.TriggerListener> listeners = new ArrayList<>();
+    Map<String, Map<AutoScaling.EventProcessorStage, List<TriggerListener>>> listenerPerStage = new HashMap<>();
+    List<TriggerListener> listeners = new ArrayList<>();
     ReentrantLock updateLock = new ReentrantLock();
 
     void setAutoScalingConfig(AutoScalingConfig autoScalingConfig) {
@@ -388,9 +388,9 @@ public class ScheduledTriggers implements Closeable {
             continue;
           }
           String clazz = config.listenerClass;
-          AutoScaling.TriggerListener listener = null;
+          TriggerListener listener = null;
           try {
-            listener = coreContainer.getResourceLoader().newInstance(clazz, AutoScaling.TriggerListener.class);
+            listener = coreContainer.getResourceLoader().newInstance(clazz, TriggerListener.class);
           } catch (Exception e) {
             log.warn("Invalid TriggerListener class name '" + clazz + "', skipping...", e);
           }
@@ -400,18 +400,15 @@ public class ScheduledTriggers implements Closeable {
           listener.init(coreContainer, config);
           listeners.add(listener);
           // add per stage
-          Map<AutoScaling.EventProcessorStage, List<AutoScaling.TriggerListener>> perStage = listenerPerStage.get(config.trigger);
-          if (perStage == null) {
-            perStage = new HashMap<>();
-            listenerPerStage.put(config.trigger, perStage);
-          }
           for (AutoScaling.EventProcessorStage stage : config.stages) {
-            List<AutoScaling.TriggerListener> lst = perStage.get(stage);
-            if (lst == null) {
-              lst = new ArrayList<>(3);
-              perStage.put(stage, lst);
-            }
-            lst.add(listener);
+            addPerStage(config.trigger, stage, listener);
+          }
+          // add also for beforeAction / afterAction TriggerStage
+          if (!config.beforeActions.isEmpty()) {
+            addPerStage(config.trigger, AutoScaling.EventProcessorStage.BEFORE_ACTION, listener);
+          }
+          if (!config.afterActions.isEmpty()) {
+            addPerStage(config.trigger, AutoScaling.EventProcessorStage.AFTER_ACTION, listener);
           }
         }
       } finally {
@@ -419,11 +416,25 @@ public class ScheduledTriggers implements Closeable {
       }
     }
 
+    private void addPerStage(String triggerName, AutoScaling.EventProcessorStage stage, TriggerListener listener) {
+      Map<AutoScaling.EventProcessorStage, List<TriggerListener>> perStage = listenerPerStage.get(triggerName);
+      if (perStage == null) {
+        perStage = new HashMap<>();
+        listenerPerStage.put(triggerName, perStage);
+      }
+      List<TriggerListener> lst = perStage.get(stage);
+      if (lst == null) {
+        lst = new ArrayList<>(3);
+        perStage.put(stage, lst);
+      }
+      lst.add(listener);
+    }
+
     void close() {
       updateLock.lock();
       try {
         listenerPerStage.clear();
-        for (AutoScaling.TriggerListener listener : listeners) {
+        for (TriggerListener listener : listeners) {
           IOUtils.closeQuietly(listener);
         }
       } finally {
@@ -431,12 +442,12 @@ public class ScheduledTriggers implements Closeable {
       }
     }
 
-    List<AutoScaling.TriggerListener> getTriggerListeners(String trigger, AutoScaling.EventProcessorStage stage) {
-      Map<AutoScaling.EventProcessorStage, List<AutoScaling.TriggerListener>> perStage = listenerPerStage.get(trigger);
+    List<TriggerListener> getTriggerListeners(String trigger, AutoScaling.EventProcessorStage stage) {
+      Map<AutoScaling.EventProcessorStage, List<TriggerListener>> perStage = listenerPerStage.get(trigger);
       if (perStage == null) {
         return Collections.emptyList();
       }
-      List<AutoScaling.TriggerListener> lst = perStage.get(stage);
+      List<TriggerListener> lst = perStage.get(stage);
       if (lst == null) {
         return Collections.emptyList();
       } else {
@@ -447,7 +458,7 @@ public class ScheduledTriggers implements Closeable {
     void fireListeners(String trigger, AutoScaling.EventProcessorStage stage, String actionName, TriggerEvent event, String message) {
       updateLock.lock();
       try {
-        for (AutoScaling.TriggerListener listener : getTriggerListeners(trigger, stage)) {
+        for (TriggerListener listener : getTriggerListeners(trigger, stage)) {
           if (actionName != null) {
             AutoScalingConfig.TriggerListenerConfig config = listener.getTriggerListenerConfig();
             if (stage == AutoScaling.EventProcessorStage.BEFORE_ACTION) {

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b52e1148/solr/core/src/java/org/apache/solr/cloud/autoscaling/TriggerActionBase.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/TriggerActionBase.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/TriggerActionBase.java
new file mode 100644
index 0000000..75c4a87
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/TriggerActionBase.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.cloud.autoscaling;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Base class for {@link TriggerAction} implementations.
+ */
+public abstract class TriggerActionBase implements TriggerAction {
+
+  protected Map<String, String> initArgs;
+
+  @Override
+  public String getName() {
+    if (initArgs != null) {
+      return initArgs.get("name");
+    } else {
+      return getClass().getSimpleName();
+    }
+  }
+
+  @Override
+  public void close() throws IOException {
+
+  }
+
+  @Override
+  public void init(Map<String, String> args) {
+    this.initArgs = args;
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b52e1148/solr/core/src/java/org/apache/solr/cloud/autoscaling/TriggerListener.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/TriggerListener.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/TriggerListener.java
new file mode 100644
index 0000000..0cd5735
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/TriggerListener.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.cloud.autoscaling;
+
+import java.io.Closeable;
+
+import org.apache.solr.core.CoreContainer;
+
+/**
+ * Implementations of this interface are notified of stages in event processing that they were
+ * registered for. Note: instances may be closed and re-created on each auto-scaling config update.
+ */
+public interface TriggerListener extends Closeable {
+
+  void init(CoreContainer coreContainer, AutoScalingConfig.TriggerListenerConfig config);
+
+  AutoScalingConfig.TriggerListenerConfig getTriggerListenerConfig();
+
+  /**
+   * This method is called when either a particular <code>stage</code> or
+   * <code>actionName</code> is reached during event processing.
+   * @param stage {@link AutoScaling.EventProcessorStage} that this listener was registered for, or null
+   * @param actionName {@link TriggerAction} name that this listener was registered for, or null
+   * @param event current event being processed
+   * @param message optional message
+   */
+  void onEvent(AutoScaling.EventProcessorStage stage, String actionName, TriggerEvent event, String message);
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b52e1148/solr/core/src/java/org/apache/solr/cloud/autoscaling/TriggerListenerBase.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/cloud/autoscaling/TriggerListenerBase.java b/solr/core/src/java/org/apache/solr/cloud/autoscaling/TriggerListenerBase.java
new file mode 100644
index 0000000..2887cec
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/cloud/autoscaling/TriggerListenerBase.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.solr.cloud.autoscaling;
+
+import java.io.IOException;
+
+import org.apache.solr.core.CoreContainer;
+
+/**
+ * Base class for implementations of {@link TriggerListener}.
+ */
+public abstract class TriggerListenerBase implements TriggerListener {
+
+  protected AutoScalingConfig.TriggerListenerConfig config;
+  protected CoreContainer coreContainer;
+
+  @Override
+  public void init(CoreContainer coreContainer, AutoScalingConfig.TriggerListenerConfig config) {
+    this.coreContainer = coreContainer;
+    this.config = config;
+  }
+
+  @Override
+  public AutoScalingConfig.TriggerListenerConfig getTriggerListenerConfig() {
+    return config;
+  }
+
+  @Override
+  public void close() throws IOException {
+
+  }
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b52e1148/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerIntegrationTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerIntegrationTest.java b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerIntegrationTest.java
index 0ceb875..4f44afb 100644
--- a/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerIntegrationTest.java
+++ b/solr/core/src/test/org/apache/solr/cloud/autoscaling/TriggerIntegrationTest.java
@@ -19,6 +19,8 @@ package org.apache.solr.cloud.autoscaling;
 
 import java.io.IOException;
 import java.lang.invoke.MethodHandles;
+import java.util.ArrayList;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -42,6 +44,7 @@ import org.apache.solr.common.cloud.ZkNodeProps;
 import org.apache.solr.common.cloud.ZkStateReader;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.Utils;
+import org.apache.solr.core.CoreContainer;
 import org.apache.solr.util.LogLevel;
 import org.apache.solr.util.TimeOut;
 import org.apache.solr.util.TimeSource;
@@ -566,18 +569,13 @@ public class TriggerIntegrationTest extends SolrCloudTestCase {
         newNode.getNodeName(), nodeAddedEvent.getProperty(TriggerEvent.NODE_NAME));
   }
 
-  public static class TestTriggerAction implements TriggerAction {
+  public static class TestTriggerAction extends TriggerActionBase {
 
     public TestTriggerAction() {
       actionConstructorCalled.countDown();
     }
 
     @Override
-    public String getName() {
-      return "TestTriggerAction";
-    }
-
-    @Override
     public void process(TriggerEvent event, ActionContext actionContext) {
       try {
         if (triggerFired.compareAndSet(false, true))  {
@@ -596,29 +594,20 @@ public class TriggerIntegrationTest extends SolrCloudTestCase {
     }
 
     @Override
-    public void close() throws IOException {
-
-    }
-
-    @Override
     public void init(Map<String, String> args) {
       log.info("TestTriggerAction init");
       actionInitCalled.countDown();
+      super.init(args);
     }
   }
 
-  public static class TestEventQueueAction implements TriggerAction {
+  public static class TestEventQueueAction extends TriggerActionBase {
 
     public TestEventQueueAction() {
       log.info("TestEventQueueAction instantiated");
     }
 
     @Override
-    public String getName() {
-      return this.getClass().getSimpleName();
-    }
-
-    @Override
     public void process(TriggerEvent event, ActionContext actionContext) {
       log.info("-- event: " + event);
       events.add(event);
@@ -634,14 +623,10 @@ public class TriggerIntegrationTest extends SolrCloudTestCase {
     }
 
     @Override
-    public void close() throws IOException {
-
-    }
-
-    @Override
     public void init(Map<String, String> args) {
       log.debug("TestTriggerAction init");
       actionInitCalled.countDown();
+      super.init(args);
     }
   }
 
@@ -790,18 +775,13 @@ public class TriggerIntegrationTest extends SolrCloudTestCase {
     return listener;
   }
 
-  public static class TestEventMarkerAction implements TriggerAction {
+  public static class TestEventMarkerAction extends TriggerActionBase {
 
     public TestEventMarkerAction() {
       actionConstructorCalled.countDown();
     }
 
     @Override
-    public String getName() {
-      return "TestEventMarkerAction";
-    }
-
-    @Override
     public void process(TriggerEvent event, ActionContext actionContext) {
       boolean locked = lock.tryLock();
       if (!locked)  {
@@ -820,14 +800,10 @@ public class TriggerIntegrationTest extends SolrCloudTestCase {
     }
 
     @Override
-    public void close() throws IOException {
-
-    }
-
-    @Override
     public void init(Map<String, String> args) {
       log.info("TestEventMarkerAction init");
       actionInitCalled.countDown();
+      super.init(args);
     }
   }
 
@@ -952,4 +928,208 @@ public class TriggerIntegrationTest extends SolrCloudTestCase {
     assertEquals(overseerLeader, ev.getProperty(TriggerEvent.NODE_NAME));
     assertEquals(AutoScaling.EventType.NODELOST, ev.getEventType());
   }
+
+  private static class TestEvent {
+    final AutoScalingConfig.TriggerListenerConfig config;
+    final AutoScaling.EventProcessorStage stage;
+    final String actionName;
+    final TriggerEvent event;
+    final String message;
+
+    TestEvent(AutoScalingConfig.TriggerListenerConfig config, AutoScaling.EventProcessorStage stage, String actionName, TriggerEvent event, String message) {
+      this.config = config;
+      this.stage = stage;
+      this.actionName = actionName;
+      this.event = event;
+      this.message = message;
+    }
+
+    @Override
+    public String toString() {
+      return "TestEvent{" +
+          "config=" + config +
+          ", stage=" + stage +
+          ", actionName='" + actionName + '\'' +
+          ", event=" + event +
+          ", message='" + message + '\'' +
+          '}';
+    }
+  }
+
+  static Map<String, List<TestEvent>> listenerEvents = new HashMap<>();
+  static CountDownLatch listenerCreated = new CountDownLatch(1);
+  static boolean failDummyAction = false;
+
+  public static class TestTriggerListener extends TriggerListenerBase {
+    @Override
+    public void init(CoreContainer coreContainer, AutoScalingConfig.TriggerListenerConfig config) {
+      super.init(coreContainer, config);
+      listenerCreated.countDown();
+    }
+
+    @Override
+    public synchronized void onEvent(AutoScaling.EventProcessorStage stage, String actionName, TriggerEvent event, String message) {
+      List<TestEvent> lst = listenerEvents.get(config.name);
+      if (lst == null) {
+        lst = new ArrayList<>();
+        listenerEvents.put(config.name, lst);
+      }
+      lst.add(new TestEvent(config, stage, actionName, event, message));
+    }
+  }
+
+  public static class TestDummyAction extends TriggerActionBase {
+
+    @Override
+    public void process(TriggerEvent event, ActionContext context) {
+      if (failDummyAction) {
+        throw new RuntimeException("failure");
+      }
+
+    }
+  }
+
+  @Test
+  public void testListeners() throws Exception {
+    CloudSolrClient solrClient = cluster.getSolrClient();
+    String setTriggerCommand = "{" +
+        "'set-trigger' : {" +
+        "'name' : 'node_added_trigger'," +
+        "'event' : 'nodeAdded'," +
+        "'waitFor' : '" + waitForSeconds + "s'," +
+        "'enabled' : true," +
+        "'actions' : [" +
+        "{'name':'test','class':'" + TestTriggerAction.class.getName() + "'}," +
+        "{'name':'test1','class':'" + TestDummyAction.class.getName() + "'}," +
+        "]" +
+        "}}";
+    SolrRequest req = createAutoScalingRequest(SolrRequest.METHOD.POST, setTriggerCommand);
+    NamedList<Object> response = solrClient.request(req);
+    assertEquals(response.get("result").toString(), "success");
+
+    if (!actionInitCalled.await(3, TimeUnit.SECONDS))  {
+      fail("The TriggerAction should have been created by now");
+    }
+
+    String setListenerCommand = "{" +
+        "'set-listener' : " +
+        "{" +
+        "'name' : 'foo'," +
+        "'trigger' : 'node_added_trigger'," +
+        "'stage' : ['WAITING', 'STARTED','ABORTED','SUCCEEDED', 'FAILED']," +
+        "'beforeAction' : 'test'," +
+        "'afterAction' : ['test', 'test1']," +
+        "'class' : '" + TestTriggerListener.class.getName() + "'" +
+        "}" +
+        "}";
+    req = createAutoScalingRequest(SolrRequest.METHOD.POST, setListenerCommand);
+    response = solrClient.request(req);
+    assertEquals(response.get("result").toString(), "success");
+
+    String setListenerCommand1 = "{" +
+        "'set-listener' : " +
+        "{" +
+        "'name' : 'bar'," +
+        "'trigger' : 'node_added_trigger'," +
+        "'stage' : ['WAITING','FAILED','SUCCEEDED']," +
+        "'beforeAction' : ['test', 'test1']," +
+        "'afterAction' : 'test'," +
+        "'class' : '" + TestTriggerListener.class.getName() + "'" +
+        "}" +
+        "}";
+    req = createAutoScalingRequest(SolrRequest.METHOD.POST, setListenerCommand1);
+    response = solrClient.request(req);
+    assertEquals(response.get("result").toString(), "success");
+
+    listenerEvents.clear();
+    failDummyAction = false;
+
+    JettySolrRunner newNode = cluster.startJettySolrRunner();
+    boolean await = triggerFiredLatch.await(20, TimeUnit.SECONDS);
+    assertTrue("The trigger did not fire at all", await);
+    assertTrue(triggerFired.get());
+
+    assertEquals("both listeners should have fired", 2, listenerEvents.size());
+
+    Thread.sleep(2000);
+
+    // check foo events
+    List<TestEvent> testEvents = listenerEvents.get("foo");
+    assertNotNull("foo events: " + testEvents, testEvents);
+    assertEquals("foo events: " + testEvents, 5, testEvents.size());
+
+    assertEquals(AutoScaling.EventProcessorStage.STARTED, testEvents.get(0).stage);
+
+    assertEquals(AutoScaling.EventProcessorStage.BEFORE_ACTION, testEvents.get(1).stage);
+    assertEquals("test", testEvents.get(1).actionName);
+
+    assertEquals(AutoScaling.EventProcessorStage.AFTER_ACTION, testEvents.get(2).stage);
+    assertEquals("test", testEvents.get(2).actionName);
+
+    assertEquals(AutoScaling.EventProcessorStage.AFTER_ACTION, testEvents.get(3).stage);
+    assertEquals("test1", testEvents.get(3).actionName);
+
+    assertEquals(AutoScaling.EventProcessorStage.SUCCEEDED, testEvents.get(4).stage);
+
+    // check bar events
+    testEvents = listenerEvents.get("bar");
+    assertNotNull("bar events", testEvents);
+    assertEquals("bar events", 4, testEvents.size());
+
+    assertEquals(AutoScaling.EventProcessorStage.BEFORE_ACTION, testEvents.get(0).stage);
+    assertEquals("test", testEvents.get(0).actionName);
+
+    assertEquals(AutoScaling.EventProcessorStage.AFTER_ACTION, testEvents.get(1).stage);
+    assertEquals("test", testEvents.get(1).actionName);
+
+    assertEquals(AutoScaling.EventProcessorStage.BEFORE_ACTION, testEvents.get(2).stage);
+    assertEquals("test1", testEvents.get(2).actionName);
+
+    assertEquals(AutoScaling.EventProcessorStage.SUCCEEDED, testEvents.get(3).stage);
+
+    // reset
+    triggerFired.set(false);
+    triggerFiredLatch = new CountDownLatch(1);
+    listenerEvents.clear();
+    failDummyAction = true;
+
+    newNode = cluster.startJettySolrRunner();
+    await = triggerFiredLatch.await(20, TimeUnit.SECONDS);
+    assertTrue("The trigger did not fire at all", await);
+
+    Thread.sleep(2000);
+
+    // check foo events
+    testEvents = listenerEvents.get("foo");
+    assertNotNull("foo events: " + testEvents, testEvents);
+    assertEquals("foo events: " + testEvents, 4, testEvents.size());
+
+    assertEquals(AutoScaling.EventProcessorStage.STARTED, testEvents.get(0).stage);
+
+    assertEquals(AutoScaling.EventProcessorStage.BEFORE_ACTION, testEvents.get(1).stage);
+    assertEquals("test", testEvents.get(1).actionName);
+
+    assertEquals(AutoScaling.EventProcessorStage.AFTER_ACTION, testEvents.get(2).stage);
+    assertEquals("test", testEvents.get(2).actionName);
+
+    assertEquals(AutoScaling.EventProcessorStage.FAILED, testEvents.get(3).stage);
+    assertEquals("test1", testEvents.get(3).actionName);
+
+    // check bar events
+    testEvents = listenerEvents.get("bar");
+    assertNotNull("bar events", testEvents);
+    assertEquals("bar events", 4, testEvents.size());
+
+    assertEquals(AutoScaling.EventProcessorStage.BEFORE_ACTION, testEvents.get(0).stage);
+    assertEquals("test", testEvents.get(0).actionName);
+
+    assertEquals(AutoScaling.EventProcessorStage.AFTER_ACTION, testEvents.get(1).stage);
+    assertEquals("test", testEvents.get(1).actionName);
+
+    assertEquals(AutoScaling.EventProcessorStage.BEFORE_ACTION, testEvents.get(2).stage);
+    assertEquals("test1", testEvents.get(2).actionName);
+
+    assertEquals(AutoScaling.EventProcessorStage.FAILED, testEvents.get(3).stage);
+    assertEquals("test1", testEvents.get(3).actionName);
+  }
 }