content: /**
   * Licensed 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.aurora.common.application;
  
  import java.util.List;
  
  import com.google.common.base.Preconditions;
  import com.google.common.collect.Lists;
  
  import org.apache.aurora.common.base.Command;
  import org.apache.aurora.common.base.ExceptionalCommand;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * A shutdown action controller. It executes actions in the reverse order they were registered, and
   * logs a warning for every shutdown action that fails, but doesn't prevent completion of subsequent
   * actions or the normal completion of the {@code execute()} method.
   *
   * @author Attila Szegedi
   */
  public interface ShutdownRegistry {
  
    /**
     * Adds an action to the shutdown registry.
     *
     * @param action Action to register.
     * @param <E> Exception type thrown by the action.
     * @param <T> Type of command.
     */
    <E extends Exception, T extends ExceptionalCommand<E>> void addAction(T action);
  
    /**
     * Implementation of a shutdown registry.
     */
    public static class ShutdownRegistryImpl implements ShutdownRegistry, Command {
      private static final Logger LOG = LoggerFactory.getLogger(ShutdownRegistryImpl.class);
  
      private final List<ExceptionalCommand<? extends Exception>> actions = Lists.newLinkedList();
  
      private boolean completed = false;
  
      /**
       * Registers an action to execute during {@link #execute()}. It is an error to call this method
       * after calling {@link #execute()}.
       *
       * @param action the action to add to the list of actions to execute during execution
       */
      @Override
      public synchronized <E extends Exception, T extends ExceptionalCommand<E>> void addAction(
          T action) {
        Preconditions.checkState(!completed);
        actions.add(action);
      }
  
      /**
       * Executes an application shutdown stage by executing all registered actions. This method can
       * be called multiple times but will only execute the registered actions the first time.
       *
       * This sends output to System.out because logging is unreliable during JVM shutdown, which
       * this class may be used for.
       */
      @Override
      public synchronized void execute() {
        if (!completed) {
          LOG.info("Executing {} shutdown commands.", actions.size());
          completed = true;
          try {
            for (ExceptionalCommand<? extends Exception> action : Lists.reverse(actions)) {
              // Part of our contract is ensuring each shutdown action executes so we must catch all
              // exceptions.
              // SUPPRESS CHECKSTYLE:OFF IllegalCatch
              try {
                action.execute();
              } catch (Exception e) {
                LOG.warn("Shutdown action failed.", e);
              }
              // SUPPRESS CHECKSTYLE:ON IllegalCatch
            }
          } finally {
            actions.clear();
          }
        } else {
          LOG.info("Action controller has already completed, subsequent calls ignored.");
        }
      }
    }
  }
  