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