001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2010 Sun Microsystems, Inc. 015 * Portions Copyright 2013-2016 ForgeRock AS. 016 */ 017package org.opends.server.api; 018 019import java.util.LinkedHashMap; 020import java.util.Map; 021import java.util.concurrent.ThreadFactory; 022import java.util.concurrent.atomic.AtomicInteger; 023import java.util.concurrent.atomic.AtomicReference; 024 025import org.forgerock.i18n.LocalizableMessage; 026import org.forgerock.i18n.slf4j.LocalizedLogger; 027import org.opends.server.backends.task.Task; 028import org.opends.server.core.DirectoryServer; 029import org.forgerock.opendj.ldap.DN; 030 031import static org.opends.messages.CoreMessages.*; 032import static org.opends.server.util.ServerConstants.*; 033import static org.opends.server.util.StaticUtils.*; 034 035/** 036 * This class defines a generic thread that should be the superclass 037 * for all threads created by the Directory Server. That is, instead 038 * of having a class that "extends Thread", you should make it 039 * "extends DirectoryThread". This provides various value-added 040 * capabilities, including: 041 * <BR> 042 * <UL> 043 * <LI>It helps make sure that all threads have a human-readable 044 * name so they are easier to identify in stack traces.</LI> 045 * <LI>It can capture a stack trace from the time that this thread 046 * was created that could be useful for debugging purposes.</LI> 047 * <LI>It plays an important role in ensuring that log messages 048 * generated as part of the processing for Directory Server 049 * tasks are properly captured and made available as part of 050 * that task.</LI> 051 * </UL> 052 */ 053@org.opends.server.types.PublicAPI( 054 stability=org.opends.server.types.StabilityLevel.VOLATILE, 055 mayInstantiate=true, 056 mayExtend=true, 057 mayInvoke=true) 058public class DirectoryThread extends Thread 059{ 060 061 /** 062 * Enumeration holding the "logical" (application) thread states, as opposed 063 * to the operating system-level java.lang.Thread.State. 064 */ 065 private static enum ThreadState 066 { 067 /** The current thread is currently not doing any useful work. */ 068 IDLE(false), 069 /** The current thread is currently processing a task, doing useful work. */ 070 PROCESSING(false), 071 /** The current thread is in the process of shutting down. */ 072 SHUTTING_DOWN(true), 073 /** 074 * The current thread has stopped running. Equivalent to 075 * java.lang.Thread.State.TERMINATED. 076 */ 077 STOPPED(true); 078 079 /** 080 * Whether this state implies a shutdown has been initiated or completed. 081 */ 082 private final boolean shutdownInitiated; 083 084 /** 085 * Constructs an instance of this enum. 086 * 087 * @param shutdownInitiated 088 * whether this state implies a shutdown was initiated. 089 */ 090 private ThreadState(boolean shutdownInitiated) 091 { 092 this.shutdownInitiated = shutdownInitiated; 093 } 094 095 /** 096 * Returns whether the current thread started the shutdown process. 097 * 098 * @return true if the current thread started the shutdown process, false 099 * otherwise. 100 */ 101 public boolean isShutdownInitiated() 102 { 103 return shutdownInitiated; 104 } 105 } 106 107 /** 108 * A factory which can be used by thread pool based services such as 109 * {@code Executor}s to dynamically create new 110 * {@code DirectoryThread} instances. 111 */ 112 public static final class Factory implements ThreadFactory 113 { 114 /** The name prefix used for all threads created using this factory. */ 115 private final String threadNamePrefix; 116 117 /** The ID to use for the next thread created using this factory. */ 118 private final AtomicInteger nextID = new AtomicInteger(); 119 120 121 /** 122 * Creates a new directory thread factory using the provided 123 * thread name prefix. 124 * 125 * @param threadNamePrefix 126 * The name prefix used for all threads created using this factory. 127 */ 128 public Factory(String threadNamePrefix) 129 { 130 if (threadNamePrefix == null) { 131 throw new NullPointerException("Null thread name prefix"); 132 } 133 134 this.threadNamePrefix = threadNamePrefix; 135 } 136 137 @Override 138 public Thread newThread(Runnable r) 139 { 140 return new DirectoryThread(r, threadNamePrefix + " " 141 + nextID.getAndIncrement()); 142 } 143 } 144 145 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 146 147 /** 148 * The directory thread group that all directory threads will be a 149 * member of. 150 */ 151 public static final DirectoryThreadGroup DIRECTORY_THREAD_GROUP = 152 new DirectoryThreadGroup(); 153 154 /** The stack trace taken at the time that this thread was created. */ 155 private StackTraceElement[] creationStackTrace; 156 157 /** The task with which this thread is associated, if any. */ 158 private Task task; 159 160 /** A reference to the thread that was used to create this thread. */ 161 private Thread parentThread; 162 163 /** The current logical thread's state. */ 164 private volatile AtomicReference<ThreadState> threadState = 165 new AtomicReference<>(ThreadState.IDLE); 166 167 /** 168 * A thread group for all directory threads. This implements a 169 * custom unhandledException handler that logs the error. 170 */ 171 private static class DirectoryThreadGroup extends ThreadGroup 172 implements AlertGenerator 173 { 174 private final LinkedHashMap<String,String> alerts = new LinkedHashMap<>(); 175 176 /** Private constructor for DirectoryThreadGroup. */ 177 private DirectoryThreadGroup() 178 { 179 super("Directory Server Thread Group"); 180 alerts.put(ALERT_TYPE_UNCAUGHT_EXCEPTION, 181 ALERT_DESCRIPTION_UNCAUGHT_EXCEPTION); 182 } 183 184 @Override 185 public DN getComponentEntryDN() { 186 return DN.rootDN(); 187 } 188 189 @Override 190 public String getClassName() { 191 return DirectoryThread.class.getName(); 192 } 193 194 @Override 195 public Map<String, String> getAlerts() { 196 return alerts; 197 } 198 199 /** 200 * Provides a means of handling a case in which a thread is about 201 * to die because of an unhandled exception. This method does 202 * nothing to try to prevent the death of that thread, but will 203 * at least log it so that it can be available for debugging 204 * purposes. 205 * 206 * @param t The thread that threw the exception. 207 * @param e The exception that was thrown but not properly 208 * handled. 209 */ 210 @Override 211 public void uncaughtException(Thread t, Throwable e) 212 { 213 if (e instanceof ThreadDeath) 214 { 215 // Ignore ThreadDeath errors that can happen when everything is being 216 // shutdown. 217 return; 218 } 219 logger.traceException(e); 220 221 LocalizableMessage message = ERR_UNCAUGHT_THREAD_EXCEPTION.get(t.getName(), stackTraceToSingleLineString(e)); 222 logger.error(message); 223 DirectoryServer.sendAlertNotification(this, 224 ALERT_TYPE_UNCAUGHT_EXCEPTION, message); 225 } 226 } 227 228 /** 229 * Creates a new instance of this directory thread with the 230 * specified name and with the specified target as its run object. 231 * 232 * @param target The target runnable object. 233 * @param threadName The human-readable name to use for this 234 * thread for debugging purposes. 235 */ 236 public DirectoryThread(Runnable target, String threadName) 237 { 238 super(DIRECTORY_THREAD_GROUP, target, threadName); 239 init(); 240 } 241 242 /** 243 * Creates a new instance of this directory thread with the 244 * specified name. 245 * 246 * @param threadName The human-readable name to use for this 247 * thread for debugging purposes. 248 */ 249 protected DirectoryThread(String threadName) 250 { 251 super(DIRECTORY_THREAD_GROUP, threadName); 252 init(); 253 } 254 255 256 257 /** 258 * Private method used to factorize constructor initialization. 259 */ 260 private void init() 261 { 262 parentThread = currentThread(); 263 creationStackTrace = parentThread.getStackTrace(); 264 265 if (parentThread instanceof DirectoryThread) 266 { 267 task = ((DirectoryThread) parentThread).task; 268 } 269 else 270 { 271 task = null; 272 } 273 274 if (DirectoryServer.getEnvironmentConfig().forceDaemonThreads()) 275 { 276 setDaemon(true); 277 } 278 } 279 280 281 282 /** 283 * Retrieves the stack trace that was captured at the time that this 284 * thread was created. 285 * 286 * @return The stack trace that was captured at the time that this 287 * thread was created. 288 */ 289 public StackTraceElement[] getCreationStackTrace() 290 { 291 return creationStackTrace; 292 } 293 294 295 296 /** 297 * Retrieves a reference to the parent thread that created this 298 * directory thread. That parent thread may or may not be a 299 * directory thread. 300 * 301 * @return A reference to the parent thread that created this 302 * directory thread. 303 */ 304 public Thread getParentThread() 305 { 306 return parentThread; 307 } 308 309 310 311 /** 312 * Retrieves the task with which this thread is associated. This 313 * will only be available for threads that are used in the process 314 * of running a task. 315 * 316 * @return The task with which this thread is associated, or 317 * {@code null} if there is none. 318 */ 319 public Task getAssociatedTask() 320 { 321 return task; 322 } 323 324 325 326 /** 327 * Sets the task with which this thread is associated. It may be 328 * {@code null} to indicate that it is not associated with any task. 329 * 330 * @param task The task with which this thread is associated. 331 */ 332 public void setAssociatedTask(Task task) 333 { 334 this.task = task; 335 } 336 337 338 /** 339 * Retrieves any relevant debug information with which this tread is 340 * associated so they can be included in debug messages. 341 * 342 * @return debug information about this thread as a string. 343 */ 344 public Map<String, String> getDebugProperties() 345 { 346 Map<String, String> properties = new LinkedHashMap<>(); 347 348 properties.put("parentThread", parentThread.getName() + 349 "(" + parentThread.getId() + ")"); 350 properties.put("isDaemon", String.valueOf(isDaemon())); 351 352 return properties; 353 } 354 355 /** 356 * Returns whether the shutdown process has been initiated on the current 357 * thread. It also returns true when the thread is actually terminated. 358 * <p> 359 * Waiting for the thread to terminate should be done by invoking one of the 360 * {@link Thread#join()} methods. 361 * 362 * @return true if the shutdown process has been initiated on the current 363 * thread, false otherwise. 364 */ 365 public boolean isShutdownInitiated() 366 { 367 return getThreadState().get().isShutdownInitiated(); 368 } 369 370 /** 371 * Instructs the current thread to initiate the shutdown process. The actual 372 * shutdown of the thread is a best effort and is dependent on the 373 * implementation of the {@link Thread#run()} method. 374 */ 375 public void initiateShutdown() 376 { 377 setThreadStateIfNotShuttingDown(ThreadState.SHUTTING_DOWN); 378 } 379 380 /** 381 * Sets the current thread state to "processing" if the shutdown process was 382 * not initiated. 383 */ 384 public void startWork() 385 { 386 setThreadStateIfNotShuttingDown(ThreadState.PROCESSING); 387 } 388 389 /** 390 * Sets the current thread state to "idle" if the shutdown process was not 391 * initiated. 392 */ 393 public void stopWork() 394 { 395 setThreadStateIfNotShuttingDown(ThreadState.IDLE); 396 } 397 398 /** 399 * Sets this thread's current state to the passed in newState if the thread is 400 * not already in a shutting down state. 401 * 402 * @param newState 403 * the new state to set 404 */ 405 private void setThreadStateIfNotShuttingDown(ThreadState newState) 406 { 407 ThreadState currentState = this.threadState.get(); 408 while (!currentState.isShutdownInitiated()) 409 { 410 if (this.threadState.compareAndSet(currentState, newState)) 411 { 412 return; 413 } 414 currentState = this.threadState.get(); 415 } 416 } 417 418 /** 419 * Returns the current thread state, possibly returning 420 * {@link ThreadState#STOPPED} if the thread is not alive. 421 * 422 * @return an {@link AtomicReference} to a ThreadState. It can be passed down 423 * as a method call parameter. 424 */ 425 private AtomicReference<ThreadState> getThreadState() 426 { 427 if (!isAlive()) 428 { 429 this.threadState.set(ThreadState.STOPPED); 430 } 431 return this.threadState; 432 } 433 434} 435