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