View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements. See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache license, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License. You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the license for the specific language governing permissions and
    15   * limitations under the license.
    16   */
    17
    18  package org.apache.logging.log4j.core.util;
    19
    20  import java.io.Serializable;
    21  import java.lang.ref.Reference;
    22  import java.lang.ref.SoftReference;
    23  import java.lang.ref.WeakReference;
    24  import java.util.Collection;
    25  import java.util.concurrent.CopyOnWriteArrayList;
    26  import java.util.concurrent.Executors;
    27  import java.util.concurrent.ThreadFactory;
    28  import java.util.concurrent.atomic.AtomicReference;
    29
    30  import org.apache.logging.log4j.Logger;
    31  import org.apache.logging.log4j.core.LifeCycle;
    32  import org.apache.logging.log4j.core.LifeCycle.State;
    33  import org.apache.logging.log4j.status.StatusLogger;
    34
    35  /**
    36   * ShutdownRegistrationStrategy that simply uses {@link Runtime#addShutdownHook(Thread)}. If no strategy is specified,
    37   * this one is used for shutdown hook registration.
    38   *
    39   * @since 2.1
    40   */
    41  public class DefaultShutdownCallbackRegistry implements ShutdownCallbackRegistry, LifeCycle, Runnable, Serializable {
    42
    43      private static final long serialVersionUID = 1L;
    44      protected static final Logger LOGGER = StatusLogger.getLogger();
    45
    46      private final AtomicReference<State> state = new AtomicReference<State>(State.INITIALIZED);
    47      private final ThreadFactory threadFactory;
    48      private final Collection<Cancellable> hooks = new CopyOnWriteArrayList<Cancellable>();
    49      private Reference<Thread> shutdownHookRef;
    50
    51      /**
    52       * Constructs a DefaultShutdownRegistrationStrategy.
    53       */
    54      public DefaultShutdownCallbackRegistry() {
    55          this(Executors.defaultThreadFactory());
    56      }
    57
    58      /**
    59       * Constructs a DefaultShutdownRegistrationStrategy using the given {@link ThreadFactory}.
    60       *
    61       * @param threadFactory the ThreadFactory to use to create a {@link Runtime} shutdown hook thread
    62       */
    63      protected DefaultShutdownCallbackRegistry(final ThreadFactory threadFactory) {
    64          this.threadFactory = threadFactory;
    65      }
    66
    67      /**
    68       * Executes the registered shutdown callbacks.
    69       */
    70      @Override
    71      public void run() {
    72          if (state.compareAndSet(State.STARTED, State.STOPPING)) {
    73              for (final Runnable hook : hooks) {
    74                  try {
    75                      hook.run();
    76                  } catch (final Throwable t) {
    77                      LOGGER.error(SHUTDOWN_HOOK_MARKER, "Caught exception executing shutdown hook {}", hook, t);
    78                  }
    79              }
    80              state.set(State.STOPPED);
    81          }
    82      }
    83
    84      @Override
    85      public Cancellable addShutdownCallback(final Runnable callback) {
    86          if (isStarted()) {
    87              final Cancellable receipt = new Cancellable() {
    88                  // use a reference to prevent memory leaks
    89                  private final Reference<Runnable> hook = new SoftReference<Runnable>(callback);
    90
    91                  @Override
    92                  public void cancel() {
    93                      hook.clear();
    94                      hooks.remove(this);
    95                  }
    96
    97                  @Override
    98                  public void run() {
    99                      final Runnable hook = this.hook.get();
    100                     if (hook != null) {
    101                         hook.run();
    102                         this.hook.clear();
    103                     }
    104                 }
    105
    106                 @Override
    107                 public String toString() {
    108                     return String.valueOf(hook.get());
    109                 }
    110             };
    111             hooks.add(receipt);
    112             return receipt;
    113         }
    114         throw new IllegalStateException("Cannot add new shutdown hook as this is not started. Current state: " +
    115             state.get().name());
    116     }
    117
    118     /**
    119      * Registers the shutdown thread only if this is initialized.
    120      */
    121     @Override
    122     public void start() {
    123         if (state.compareAndSet(State.INITIALIZED, State.STARTING)) {
    124             try {
    125                 addShutdownHook(threadFactory.newThread(this));
    126                 state.set(State.STARTED);
    127             } catch (final Exception e) {
    128                 LOGGER.catching(e);
    129                 state.set(State.STOPPED);
    130             }
    131         }
    132     }
    133
    134     private void addShutdownHook(final Thread thread) {
    135         shutdownHookRef = new WeakReference<Thread>(thread);
    136         Runtime.getRuntime().addShutdownHook(thread);
    137     }
    138
    139     /**
    140      * Cancels the shutdown thread only if this is started.
    141      */
    142     @Override
    143     public void stop() {
    144         if (state.compareAndSet(State.STARTED, State.STOPPING)) {
    145             try {
    146                 removeShutdownHook();
    147             } finally {
    148                 state.set(State.STOPPED);
    149             }
    150         }
    151     }
    152
    153     private void removeShutdownHook() {
    154         final Thread shutdownThread = shutdownHookRef.get();
    155         if (shutdownThread != null) {
    156             Runtime.getRuntime().removeShutdownHook(shutdownThread);
    157             shutdownHookRef.enqueue();
    158         }
    159     }
    160
    161     @Override
    162     public State getState() {
    163         return state.get();
    164     }
    165
    166     /**
    167      * Indicates if this can accept shutdown hooks.
    168      *
    169      * @return true if this can accept shutdown hooks
    170      */
    171     @Override
    172     public boolean isStarted() {
    173         return state.get() == State.STARTED;
    174     }
    175
    176     @Override
    177     public boolean isStopped() {
    178         return state.get() == State.STOPPED;
    179     }
    180
    181 }