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