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-2009 Sun Microsystems, Inc.
015 * Portions Copyright 2013-2014 ForgeRock AS.
016 */
017package org.opends.server.backends.task;
018
019import java.util.Map;
020
021import org.forgerock.i18n.LocalizableMessage;
022import org.opends.server.api.DirectoryThread;
023import org.forgerock.i18n.slf4j.LocalizedLogger;
024
025import static org.opends.messages.BackendMessages.*;
026import static org.opends.server.util.StaticUtils.*;
027
028/**
029 * This class defines a thread that will be used to execute a scheduled task
030 * within the server and provide appropriate notification that the task is
031 * complete.
032 */
033public class TaskThread extends DirectoryThread
034{
035  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
036
037
038
039  /** Indicates whether a request has been made for this thread to exit. */
040  private volatile boolean exitRequested;
041
042  /** The thread ID for this task thread. */
043  private int threadID;
044
045  /** The reference to the scheduler with which this thread is associated. */
046  private TaskScheduler taskScheduler;
047
048  /**
049   * The object that will be used for signaling the thread when there is new
050   * work to perform.
051   */
052  private final Object notifyLock;
053
054
055
056  /**
057   * Creates a new task thread with the provided information.
058   *
059   * @param  taskScheduler  The reference to the task scheduler with which this
060   *                        thread is associated.
061   * @param  threadID       The ID assigned to this task thread.
062   */
063  public TaskThread(TaskScheduler taskScheduler, int threadID)
064  {
065    super("Task Thread " + threadID);
066
067
068    this.taskScheduler = taskScheduler;
069    this.threadID      = threadID;
070
071    notifyLock    = new Object();
072    exitRequested = false;
073
074    setAssociatedTask(null);
075  }
076
077
078
079  /**
080   * Retrieves the task currently being processed by this thread, if it is
081   * active.
082   *
083   * @return  The task currently being processed by this thread, or
084   *          <CODE>null</CODE> if it is not processing any task.
085   */
086  public Task getTask()
087  {
088    return getAssociatedTask();
089  }
090
091
092
093  /**
094   * Provides a new task for processing by this thread.  This does not do any
095   * check to ensure that no task is already in process.
096   *
097   * @param  task  The task to be processed.
098   */
099  public void setTask(Task task)
100  {
101    setAssociatedTask(task);
102
103    synchronized (notifyLock)
104    {
105      notifyLock.notify();
106    }
107  }
108
109
110
111  /**
112   * Attempts to interrupt processing on the task in progress.
113   *
114   * @param  interruptState   The state to use for the task if it is
115   *                          successfully interrupted.
116   * @param  interruptReason  The human-readable reason that the task is to be
117   *                          interrupted.
118   * @param  exitThread       Indicates whether this thread should exit when
119   *                          processing on the active task has completed.
120   */
121  public void interruptTask(TaskState interruptState, LocalizableMessage interruptReason,
122                            boolean exitThread)
123  {
124    if (getAssociatedTask() != null)
125    {
126      try
127      {
128        getAssociatedTask().interruptTask(interruptState, interruptReason);
129      }
130      catch (Exception e)
131      {
132        logger.traceException(e);
133      }
134    }
135
136    if (exitThread)
137    {
138      exitRequested = true;
139    }
140  }
141
142
143
144  /**
145   * Operates in a loop, sleeping until there is no work to do, then
146   * processing the task and returning to the scheduler for more work.
147   */
148  @Override
149  public void run()
150  {
151    while (! exitRequested)
152    {
153      if (getAssociatedTask() == null)
154      {
155        try
156        {
157          synchronized (notifyLock)
158          {
159            notifyLock.wait(5000);
160          }
161        }
162        catch (InterruptedException ie)
163        {
164          logger.traceException(ie);
165        }
166
167        continue;
168      }
169
170      TaskState taskState = getAssociatedTask().getTaskState();
171      try
172      {
173        if (!TaskState.isDone(taskState))
174        {
175          Task task = getAssociatedTask();
176
177          logger.info(NOTE_TASK_STARTED, task.getDisplayName(), task.getTaskID());
178
179          taskState = task.execute();
180
181          logger.info(NOTE_TASK_FINISHED, task.getDisplayName(),
182              task.getTaskID(), taskState.getDisplayName());
183        }
184      }
185      catch (Exception e)
186      {
187        logger.traceException(e);
188
189        Task task = getAssociatedTask();
190        logger.error(ERR_TASK_EXECUTE_FAILED, task.getTaskEntry().getName(), stackTraceToSingleLineString(e));
191        task.setTaskState(TaskState.STOPPED_BY_ERROR);
192      }
193
194      Task completedTask = getAssociatedTask();
195      setAssociatedTask(null);
196      if (! taskScheduler.threadDone(this, completedTask, taskState))
197      {
198        exitRequested = true;
199        break;
200      }
201    }
202
203    if (getAssociatedTask() != null)
204    {
205      Task task = getAssociatedTask();
206      TaskState taskState = TaskState.STOPPED_BY_SHUTDOWN;
207      taskScheduler.threadDone(this, task, taskState);
208    }
209  }
210
211
212
213  /**
214   * Retrieves any relevant debug information with which this tread is
215   * associated so they can be included in debug messages.
216   *
217   * @return debug information about this thread as a string.
218   */
219  @Override
220  public Map<String, String> getDebugProperties()
221  {
222    Map<String, String> properties = super.getDebugProperties();
223
224    if (getAssociatedTask() != null)
225    {
226      properties.put("task", getAssociatedTask().toString());
227    }
228
229    return properties;
230  }
231}
232