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      private final ThreadNameDeterminer determiner;
38  
39      /**
40       * Returns the {@link ThreadNameDeterminer} which overrides the proposed
41       * new thread name.
42       */
43      public static ThreadNameDeterminer getThreadNameDeterminer() {
44          return threadNameDeterminer;
45      }
46  
47      /**
48       * Sets the {@link ThreadNameDeterminer} which overrides the proposed new
49       * thread name.  Please note that the specified {@link ThreadNameDeterminer}
50       * affects only new {@link ThreadRenamingRunnable}s; the existing instances
51       * are not affected at all.  Therefore, you should make sure to call this
52       * method at the earliest possible point (i.e. before any Netty worker
53       * thread starts) for consistent thread naming.  Otherwise, you might see
54       * the default thread names and the new names appear at the same time in
55       * the full thread dump.
56       */
57      public static void setThreadNameDeterminer(ThreadNameDeterminer threadNameDeterminer) {
58          if (threadNameDeterminer == null) {
59              throw new NullPointerException("threadNameDeterminer");
60          }
61          ThreadRenamingRunnable.threadNameDeterminer = threadNameDeterminer;
62      }
63  
64      private final Runnable runnable;
65      private final String proposedThreadName;
66  
67      /**
68       * Creates a new instance which wraps the specified {@code runnable}
69       * and changes the thread name to the specified thread name when the
70       * specified {@code runnable} is running.
71       */
72      public ThreadRenamingRunnable(Runnable runnable, String proposedThreadName, ThreadNameDeterminer determiner) {
73          if (runnable == null) {
74              throw new NullPointerException("runnable");
75          }
76          if (proposedThreadName == null) {
77              throw new NullPointerException("proposedThreadName");
78          }
79          this.runnable = runnable;
80          this.determiner = determiner;
81          this.proposedThreadName = proposedThreadName;
82      }
83  
84      public ThreadRenamingRunnable(Runnable runnable, String proposedThreadName) {
85          this(runnable, proposedThreadName, null);
86      }
87  
88      public void run() {
89          final Thread currentThread = Thread.currentThread();
90          final String oldThreadName = currentThread.getName();
91          final String newThreadName = getNewThreadName(oldThreadName);
92  
93          // Change the thread name before starting the actual runnable.
94          boolean renamed = false;
95          if (!oldThreadName.equals(newThreadName)) {
96              try {
97                  currentThread.setName(newThreadName);
98                  renamed = true;
99              } catch (SecurityException e) {
100                 logger.debug(
101                         "Failed to rename a thread " +
102                         "due to security restriction.", e);
103             }
104         }
105 
106         // Run the actual runnable and revert the name back when it ends.
107         try {
108             runnable.run();
109         } finally {
110             if (renamed) {
111                 // Revert the name back if the current thread was renamed.
112                 // We do not check the exception here because we know it works.
113                 currentThread.setName(oldThreadName);
114             }
115         }
116     }
117 
118     private String getNewThreadName(String currentThreadName) {
119         String newThreadName = null;
120 
121         try {
122             ThreadNameDeterminer nameDeterminer = determiner;
123             if (nameDeterminer == null) {
124                 nameDeterminer = getThreadNameDeterminer();
125             }
126             newThreadName =
127                 nameDeterminer.determineThreadName(
128                         currentThreadName, proposedThreadName);
129         } catch (Throwable t) {
130             logger.warn("Failed to determine the thread name", t);
131         }
132 
133         return newThreadName == null? currentThreadName : newThreadName;
134     }
135 }