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 }