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