001/*
002 * Copyright (C) 2010 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License
010 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
011 * or implied. See the License for the specific language governing permissions and limitations under
012 * the License.
013 */
014
015package com.google.common.util.concurrent;
016
017import static java.util.logging.Level.SEVERE;
018
019import com.google.common.annotations.GwtIncompatible;
020import com.google.common.annotations.J2ktIncompatible;
021import com.google.common.annotations.VisibleForTesting;
022import java.lang.Thread.UncaughtExceptionHandler;
023import java.util.Locale;
024
025/**
026 * Factories for {@link UncaughtExceptionHandler} instances.
027 *
028 * @author Gregory Kick
029 * @since 8.0
030 */
031@J2ktIncompatible
032@GwtIncompatible
033public final class UncaughtExceptionHandlers {
034  private UncaughtExceptionHandlers() {}
035
036  /**
037   * Returns an exception handler that exits the system. This is particularly useful for the main
038   * thread, which may start up other, non-daemon threads, but fail to fully initialize the
039   * application successfully.
040   *
041   * <p>Example usage:
042   *
043   * <pre>
044   * public static void main(String[] args) {
045   *   Thread.currentThread().setUncaughtExceptionHandler(UncaughtExceptionHandlers.systemExit());
046   *   ...
047   * </pre>
048   *
049   * <p>The returned handler logs any exception at severity {@code SEVERE} and then shuts down the
050   * process with an exit status of 1, indicating abnormal termination.
051   */
052  public static UncaughtExceptionHandler systemExit() {
053    return new Exiter(Runtime.getRuntime()::exit);
054  }
055
056  @VisibleForTesting
057  interface RuntimeWrapper {
058    void exit(int status);
059  }
060
061  @VisibleForTesting
062  static final class Exiter implements UncaughtExceptionHandler {
063    private static final LazyLogger logger = new LazyLogger(Exiter.class);
064
065    private final RuntimeWrapper runtime;
066
067    Exiter(RuntimeWrapper runtime) {
068      this.runtime = runtime;
069    }
070
071    @Override
072    public void uncaughtException(Thread t, Throwable e) {
073      try {
074        logger
075            .get()
076            .log(
077                SEVERE,
078                String.format(Locale.ROOT, "Caught an exception in %s.  Shutting down.", t),
079                e);
080      } catch (Throwable errorInLogging) { // sneaky checked exception
081        // If logging fails, e.g. due to missing memory, at least try to log the
082        // message and the cause for the failed logging.
083        System.err.println(e.getMessage());
084        System.err.println(errorInLogging.getMessage());
085      } finally {
086        runtime.exit(1);
087      }
088    }
089  }
090}