mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Chris Ridd
12.25.2016 3139679a516721b5a31c0f40458679a72d861263
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
/*
 * The contents of this file are subject to the terms of the Common Development and
 * Distribution License (the License). You may not use this file except in compliance with the
 * License.
 *
 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
 * specific language governing permission and limitations under the License.
 *
 * When distributing Covered Software, include this CDDL Header Notice in each file and include
 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2006-2010 Sun Microsystems, Inc.
 * Portions Copyright 2013-2016 ForgeRock AS.
 */
package org.opends.server.api;
 
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
 
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.opends.server.backends.task.Task;
import org.opends.server.core.DirectoryServer;
import org.forgerock.opendj.ldap.DN;
 
import static org.opends.messages.CoreMessages.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
 
/**
 * This class defines a generic thread that should be the superclass
 * for all threads created by the Directory Server.  That is, instead
 * of having a class that "extends Thread", you should make it
 * "extends DirectoryThread".  This provides various value-added
 * capabilities, including:
 * <BR>
 * <UL>
 *   <LI>It helps make sure that all threads have a human-readable
 *       name so they are easier to identify in stack traces.</LI>
 *   <LI>It can capture a stack trace from the time that this thread
 *       was created that could be useful for debugging purposes.</LI>
 *   <LI>It plays an important role in ensuring that log messages
 *       generated as part of the processing for Directory Server
 *       tasks are properly captured and made available as part of
 *       that task.</LI>
 * </UL>
 */
@org.opends.server.types.PublicAPI(
     stability=org.opends.server.types.StabilityLevel.VOLATILE,
     mayInstantiate=true,
     mayExtend=true,
     mayInvoke=true)
public class DirectoryThread extends Thread
{
 
  /**
   * Enumeration holding the "logical" (application) thread states, as opposed
   * to the operating system-level java.lang.Thread.State.
   */
  private static enum ThreadState
  {
    /** The current thread is currently not doing any useful work. */
    IDLE(false),
    /** The current thread is currently processing a task, doing useful work. */
    PROCESSING(false),
    /** The current thread is in the process of shutting down. */
    SHUTTING_DOWN(true),
    /**
     * The current thread has stopped running. Equivalent to
     * java.lang.Thread.State.TERMINATED.
     */
    STOPPED(true);
 
    /**
     * Whether this state implies a shutdown has been initiated or completed.
     */
    private final boolean shutdownInitiated;
 
    /**
     * Constructs an instance of this enum.
     *
     * @param shutdownInitiated
     *          whether this state implies a shutdown was initiated.
     */
    private ThreadState(boolean shutdownInitiated)
    {
      this.shutdownInitiated = shutdownInitiated;
    }
 
    /**
     * Returns whether the current thread started the shutdown process.
     *
     * @return true if the current thread started the shutdown process, false
     *         otherwise.
     */
    public boolean isShutdownInitiated()
    {
      return shutdownInitiated;
    }
  }
 
  /**
   * A factory which can be used by thread pool based services such as
   * {@code Executor}s to dynamically create new
   * {@code DirectoryThread} instances.
   */
  public static final class Factory implements ThreadFactory
  {
    /** The name prefix used for all threads created using this factory. */
    private final String threadNamePrefix;
 
    /** The ID to use for the next thread created using this factory. */
    private final AtomicInteger nextID = new AtomicInteger();
 
 
    /**
     * Creates a new directory thread factory using the provided
     * thread name prefix.
     *
     * @param threadNamePrefix
     *          The name prefix used for all threads created using this factory.
     */
    public Factory(String threadNamePrefix)
    {
      if (threadNamePrefix == null) {
        throw new NullPointerException("Null thread name prefix");
      }
 
      this.threadNamePrefix = threadNamePrefix;
    }
 
    @Override
    public Thread newThread(Runnable r)
    {
      return new DirectoryThread(r, threadNamePrefix + " "
          + nextID.getAndIncrement());
    }
  }
 
  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
 
  /**
   * The directory thread group that all directory threads will be a
   * member of.
   */
  public static final DirectoryThreadGroup DIRECTORY_THREAD_GROUP =
      new DirectoryThreadGroup();
 
  /** The stack trace taken at the time that this thread was created. */
  private StackTraceElement[] creationStackTrace;
 
  /** The task with which this thread is associated, if any. */
  private Task task;
 
  /** A reference to the thread that was used to create this thread. */
  private Thread parentThread;
 
  /** The current logical thread's state. */
  private volatile AtomicReference<ThreadState> threadState =
      new AtomicReference<>(ThreadState.IDLE);
 
  /**
   * A thread group for all directory threads. This implements a
   * custom unhandledException handler that logs the error.
   */
  private static class DirectoryThreadGroup extends ThreadGroup
      implements AlertGenerator
  {
    private final LinkedHashMap<String,String> alerts = new LinkedHashMap<>();
 
    /** Private constructor for DirectoryThreadGroup. */
    private DirectoryThreadGroup()
    {
      super("Directory Server Thread Group");
      alerts.put(ALERT_TYPE_UNCAUGHT_EXCEPTION,
          ALERT_DESCRIPTION_UNCAUGHT_EXCEPTION);
    }
 
    @Override
    public DN getComponentEntryDN() {
      return DN.rootDN();
    }
 
    @Override
    public String getClassName() {
      return DirectoryThread.class.getName();
    }
 
    @Override
    public Map<String, String> getAlerts() {
      return alerts;
    }
 
    /**
     * Provides a means of handling a case in which a thread is about
     * to die because of an unhandled exception.  This method does
     * nothing to try to prevent the death of that thread, but will
     * at least log it so that it can be available for debugging
     * purposes.
     *
     * @param  t  The thread that threw the exception.
     * @param  e  The exception that was thrown but not properly
     *            handled.
     */
    @Override
    public void uncaughtException(Thread t, Throwable e)
    {
      if (e instanceof ThreadDeath)
      {
        // Ignore ThreadDeath errors that can happen when everything is being
        // shutdown.
        return;
      }
      logger.traceException(e);
 
      LocalizableMessage message = ERR_UNCAUGHT_THREAD_EXCEPTION.get(t.getName(), stackTraceToSingleLineString(e));
      logger.error(message);
      DirectoryServer.sendAlertNotification(this,
          ALERT_TYPE_UNCAUGHT_EXCEPTION, message);
    }
  }
 
  /**
   * Creates a new instance of this directory thread with the
   * specified name and with the specified target as its run object.
   *
   * @param  target      The target runnable object.
   * @param  threadName  The human-readable name to use for this
   *                     thread for debugging purposes.
   */
  public DirectoryThread(Runnable target, String threadName)
  {
    super(DIRECTORY_THREAD_GROUP, target, threadName);
    init();
  }
 
  /**
   * Creates a new instance of this directory thread with the
   * specified name.
   *
   * @param  threadName  The human-readable name to use for this
   *                     thread for debugging purposes.
   */
  protected DirectoryThread(String threadName)
  {
    super(DIRECTORY_THREAD_GROUP, threadName);
    init();
  }
 
 
 
  /**
   * Private method used to factorize constructor initialization.
   */
  private void init()
  {
    parentThread       = currentThread();
    creationStackTrace = parentThread.getStackTrace();
 
    if (parentThread instanceof DirectoryThread)
    {
      task = ((DirectoryThread) parentThread).task;
    }
    else
    {
      task = null;
    }
 
    if (DirectoryServer.getEnvironmentConfig().forceDaemonThreads())
    {
      setDaemon(true);
    }
  }
 
 
 
  /**
   * Retrieves the stack trace that was captured at the time that this
   * thread was created.
   *
   * @return  The stack trace that was captured at the time that this
   *          thread was created.
   */
  public StackTraceElement[] getCreationStackTrace()
  {
    return creationStackTrace;
  }
 
 
 
  /**
   * Retrieves a reference to the parent thread that created this
   * directory thread.  That parent thread may or may not be a
   * directory thread.
   *
   * @return  A reference to the parent thread that created this
   *          directory thread.
   */
  public Thread getParentThread()
  {
    return parentThread;
  }
 
 
 
  /**
   * Retrieves the task with which this thread is associated.  This
   * will only be available for threads that are used in the process
   * of running a task.
   *
   * @return  The task with which this thread is associated, or
   *          {@code null} if there is none.
   */
  public Task getAssociatedTask()
  {
    return task;
  }
 
 
 
  /**
   * Sets the task with which this thread is associated.  It may be
   * {@code null} to indicate that it is not associated with any task.
   *
   * @param  task  The task with which this thread is associated.
   */
  public void setAssociatedTask(Task task)
  {
    this.task = task;
  }
 
 
  /**
   * Retrieves any relevant debug information with which this tread is
   * associated so they can be included in debug messages.
   *
   * @return debug information about this thread as a string.
   */
  public Map<String, String> getDebugProperties()
  {
    Map<String, String> properties = new LinkedHashMap<>();
 
    properties.put("parentThread", parentThread.getName() +
        "(" + parentThread.getId() + ")");
    properties.put("isDaemon", String.valueOf(isDaemon()));
 
    return properties;
  }
 
  /**
   * Returns whether the shutdown process has been initiated on the current
   * thread. It also returns true when the thread is actually terminated.
   * <p>
   * Waiting for the thread to terminate should be done by invoking one of the
   * {@link Thread#join()} methods.
   *
   * @return true if the shutdown process has been initiated on the current
   *         thread, false otherwise.
   */
  public boolean isShutdownInitiated()
  {
    return getThreadState().get().isShutdownInitiated();
  }
 
  /**
   * Instructs the current thread to initiate the shutdown process. The actual
   * shutdown of the thread is a best effort and is dependent on the
   * implementation of the {@link Thread#run()} method.
   */
  public void initiateShutdown()
  {
    setThreadStateIfNotShuttingDown(ThreadState.SHUTTING_DOWN);
  }
 
  /**
   * Sets the current thread state to "processing" if the shutdown process was
   * not initiated.
   */
  public void startWork()
  {
    setThreadStateIfNotShuttingDown(ThreadState.PROCESSING);
  }
 
  /**
   * Sets the current thread state to "idle" if the shutdown process was not
   * initiated.
   */
  public void stopWork()
  {
    setThreadStateIfNotShuttingDown(ThreadState.IDLE);
  }
 
  /**
   * Sets this thread's current state to the passed in newState if the thread is
   * not already in a shutting down state.
   *
   * @param newState
   *          the new state to set
   */
  private void setThreadStateIfNotShuttingDown(ThreadState newState)
  {
    ThreadState currentState = this.threadState.get();
    while (!currentState.isShutdownInitiated())
    {
      if (this.threadState.compareAndSet(currentState, newState))
      {
        return;
      }
      currentState = this.threadState.get();
    }
  }
 
  /**
   * Returns the current thread state, possibly returning
   * {@link ThreadState#STOPPED} if the thread is not alive.
   *
   * @return an {@link AtomicReference} to a ThreadState. It can be passed down
   *         as a method call parameter.
   */
  private AtomicReference<ThreadState> getThreadState()
  {
    if (!isAlive())
    {
      this.threadState.set(ThreadState.STOPPED);
    }
    return this.threadState;
  }
 
}