View Javadoc

    1   /*
    2    * Copyright 2012 The Netty Project
    3    *
    4    * The Netty Project licenses this file to you under the Apache License,
    5    * version 2.0 (the "License"); you may not use this file except in compliance
    6    * with the License. You may obtain a copy of the License at:
    7    *
    8    *   http://www.apache.org/licenses/LICENSE-2.0
    9    *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    13   * License for the specific language governing permissions and limitations
    14   * under the License.
    15   */
    16  package org.jboss.netty.util;
    17
    18  import org.jboss.netty.logging.InternalLogger;
    19  import org.jboss.netty.logging.InternalLoggerFactory;
    20
    21
    22  /**
    23   * A {@link Runnable} that changes the current thread name and reverts it back
    24   * when its execution ends.  To change the default thread names set by Netty,
    25   * use {@link #setThreadNameDeterminer(ThreadNameDeterminer)}.
    26   *
    27   * @apiviz.landmark
    28   * @apiviz.has org.jboss.netty.util.ThreadNameDeterminer oneway - -
    29   */
    30  public class ThreadRenamingRunnable implements Runnable {
    31
    32      private static final InternalLogger logger =
    33          InternalLoggerFactory.getInstance(ThreadRenamingRunnable.class);
    34
    35      private static volatile ThreadNameDeterminer threadNameDeterminer =
    36          ThreadNameDeterminer.PROPOSED;
    37
    38      /**
    39       * Returns the {@link ThreadNameDeterminer} which overrides the proposed
    40       * new thread name.
    41       */
    42      public static ThreadNameDeterminer getThreadNameDeterminer() {
    43          return threadNameDeterminer;
    44      }
    45
    46      /**
    47       * Sets the {@link ThreadNameDeterminer} which overrides the proposed new
    48       * thread name.  Please note that the specified {@link ThreadNameDeterminer}
    49       * affects only new {@link ThreadRenamingRunnable}s; the existing instances
    50       * are not affected at all.  Therefore, you should make sure to call this
    51       * method at the earliest possible point (i.e. before any Netty worker
    52       * thread starts) for consistent thread naming.  Otherwise, you might see
    53       * the default thread names and the new names appear at the same time in
    54       * the full thread dump.
    55       */
    56      public static void setThreadNameDeterminer(ThreadNameDeterminer threadNameDeterminer) {
    57          if (threadNameDeterminer == null) {
    58              throw new NullPointerException("threadNameDeterminer");
    59          }
    60          ThreadRenamingRunnable.threadNameDeterminer = threadNameDeterminer;
    61      }
    62
    63      private final Runnable runnable;
    64      private final String proposedThreadName;
    65
    66      /**
    67       * Creates a new instance which wraps the specified {@code runnable}
    68       * and changes the thread name to the specified thread name when the
    69       * specified {@code runnable} is running.
    70       */
    71      public ThreadRenamingRunnable(Runnable runnable, String proposedThreadName) {
    72          if (runnable == null) {
    73              throw new NullPointerException("runnable");
    74          }
    75          if (proposedThreadName == null) {
    76              throw new NullPointerException("proposedThreadName");
    77          }
    78          this.runnable = runnable;
    79          this.proposedThreadName = proposedThreadName;
    80      }
    81
    82      public void run() {
    83          final Thread currentThread = Thread.currentThread();
    84          final String oldThreadName = currentThread.getName();
    85          final String newThreadName = getNewThreadName(oldThreadName);
    86
    87          // Change the thread name before starting the actual runnable.
    88          boolean renamed = false;
    89          if (!oldThreadName.equals(newThreadName)) {
    90              try {
    91                  currentThread.setName(newThreadName);
    92                  renamed = true;
    93              } catch (SecurityException e) {
    94                  logger.debug(
    95                          "Failed to rename a thread " +
    96                          "due to security restriction.", e);
    97              }
    98          }
    99
    100         // Run the actual runnable and revert the name back when it ends.
    101         try {
    102             runnable.run();
    103         } finally {
    104             if (renamed) {
    105                 // Revert the name back if the current thread was renamed.
    106                 // We do not check the exception here because we know it works.
    107                 currentThread.setName(oldThreadName);
    108             }
    109         }
    110     }
    111
    112     private String getNewThreadName(String currentThreadName) {
    113         String newThreadName = null;
    114
    115         try {
    116             newThreadName =
    117                 getThreadNameDeterminer().determineThreadName(
    118                         currentThreadName, proposedThreadName);
    119         } catch (Throwable t) {
    120             logger.warn("Failed to determine the thread name", t);
    121         }
    122
    123         return newThreadName == null? currentThreadName : newThreadName;
    124     }
    125 }