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