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
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2008 Sun Microsystems, Inc.
026 */
027
028 package org.opends.server.tools.tasks;
029
030 import org.opends.messages.Message;
031
032 import org.opends.server.backends.task.Task;
033 import org.opends.server.backends.task.TaskState;
034 import org.opends.server.backends.task.FailedDependencyAction;
035 import org.opends.server.types.Entry;
036 import org.opends.server.types.AttributeType;
037 import org.opends.server.types.Attribute;
038 import org.opends.server.types.AttributeValue;
039 import org.opends.server.types.DN;
040 import static org.opends.server.util.ServerConstants.*;
041
042 import java.util.Map;
043 import java.util.HashMap;
044 import java.util.Set;
045 import java.util.HashSet;
046 import java.util.List;
047 import java.util.LinkedHashSet;
048 import java.util.ArrayList;
049 import java.util.TimeZone;
050 import java.util.Date;
051 import java.util.Collections;
052 import java.lang.reflect.Method;
053 import java.text.SimpleDateFormat;
054 import java.text.DateFormat;
055 import java.text.ParseException;
056
057 /**
058 * Processes information from a task entry from the directory and
059 * provides accessors for attribute information. In some cases the
060 * data is formatted into more human-friendly formats.
061 */
062 public class TaskEntry {
063
064 private static Map<String, Message> mapClassToTypeName =
065 new HashMap<String, Message>();
066
067 private static Map<String, Message> mapAttrToDisplayName =
068 new HashMap<String, Message>();
069
070
071 // These attributes associated with the ds-task object
072 // class are all handled explicitly below in the constructor
073 private static Set<String> supAttrNames = new HashSet<String>();
074 static {
075 supAttrNames.add("ds-task-id");
076 supAttrNames.add("ds-task-class-name");
077 supAttrNames.add("ds-task-state");
078 supAttrNames.add("ds-task-scheduled-start-time");
079 supAttrNames.add("ds-task-actual-start-time");
080 supAttrNames.add("ds-task-completion-time");
081 supAttrNames.add("ds-task-dependency-id");
082 supAttrNames.add("ds-task-failed-dependency-action");
083 supAttrNames.add("ds-task-log-message");
084 supAttrNames.add("ds-task-notify-on-completion");
085 supAttrNames.add("ds-task-notify-on-error");
086 }
087
088 private String id;
089 private String className;
090 private String state;
091 private String schedStart;
092 private String actStart;
093 private String compTime;
094 private List<String> depends;
095 private String depFailAct;
096 private List<String> logs;
097 private List<String> notifyComp;
098 private List<String> notifyErr;
099 private DN dn;
100
101 /**
102 * Task of the same type that implements. Used for obtaining
103 * task name and attribute display information.
104 */
105 private Task task;
106
107 private Map<Message, List<String>> taskSpecificAttrValues =
108 new HashMap<Message, List<String>>();
109
110 /**
111 * Creates a parameterized instance.
112 *
113 * @param entry to wrap
114 */
115 public TaskEntry(Entry entry) {
116 dn = entry.getDN();
117
118 String p = "ds-task-";
119 id = getSingleStringValue(entry, p + "id");
120 className = getSingleStringValue(entry, p + "class-name");
121 state = getSingleStringValue(entry, p + "state");
122 schedStart = getSingleStringValue(entry, p + "scheduled-start-time");
123 actStart = getSingleStringValue(entry, p + "actual-start-time");
124 compTime = getSingleStringValue(entry, p + "completion-time");
125 depends = getMultiStringValue(entry, p + "dependency-id");
126 depFailAct = getSingleStringValue(entry, p + "failed-dependency-action");
127 logs = getMultiStringValue(entry, p + "log-message");
128 notifyErr = getMultiStringValue(entry, p + "notify-on-error");
129 notifyComp = getMultiStringValue(entry, p + "notify-on-completion");
130
131
132 // Build a map of non-superior attribute value pairs for display
133 Map<AttributeType, List<Attribute>> attrMap = entry.getUserAttributes();
134 for (AttributeType type : attrMap.keySet()) {
135 String typeName = type.getNormalizedPrimaryName();
136
137 // See if we've handled it already above
138 if (!supAttrNames.contains(typeName)) {
139 Message attrTypeName = getAttributeDisplayName(
140 type.getNormalizedPrimaryName());
141 List<Attribute> attrList = entry.getUserAttribute(type);
142 for (Attribute attr : attrList) {
143 LinkedHashSet<AttributeValue> valuesSet = attr.getValues();
144 for (AttributeValue av : valuesSet) {
145 List<String> valueList = taskSpecificAttrValues.get(attrTypeName);
146 if (valueList == null) {
147 valueList = new ArrayList<String>();
148 taskSpecificAttrValues.put(attrTypeName, valueList);
149 }
150 valueList.add(av.getStringValue());
151 }
152 }
153 }
154 }
155 }
156
157 /**
158 * Gets the DN of the wrapped entry.
159 *
160 * @return DN of entry
161 */
162 public DN getDN() {
163 return dn;
164 }
165
166 /**
167 * Gets the ID of the task.
168 *
169 * @return String ID of the task
170 */
171 public String getId() {
172 return id;
173 }
174
175 /**
176 * Gets the name of the class implementing the task represented here.
177 *
178 * @return String name of class
179 */
180 public String getClassName() {
181 return className;
182 }
183
184 /**
185 * Gets the state of the task.
186 *
187 * @return Message representing state
188 */
189 public Message getState() {
190 Message m = Message.EMPTY;
191 if (state != null) {
192 TaskState ts = TaskState.fromString(state);
193 if (ts != null) {
194 m = ts.getDisplayName();
195 }
196 }
197 return m;
198 }
199
200 /**
201 * Gets the human-friendly scheduled time.
202 *
203 * @return String time
204 */
205 public Message getScheduledStartTime() {
206 return formatTimeString(schedStart);
207 }
208
209 /**
210 * Gets the human-friendly start time.
211 *
212 * @return String time
213 */
214 public Message getActualStartTime() {
215 return formatTimeString(actStart);
216 }
217
218 /**
219 * Gets the human-friendly completion time.
220 *
221 * @return String time
222 */
223 public Message getCompletionTime() {
224 return formatTimeString(compTime);
225 }
226
227 /**
228 * Gets the IDs of tasks upon which this task depends.
229 *
230 * @return array of IDs
231 */
232 public List<String> getDependencyIds() {
233 return Collections.unmodifiableList(depends);
234 }
235
236 /**
237 * Gets the action to take if this task fails.
238 *
239 * @return String action
240 */
241 public Message getFailedDependencyAction() {
242 Message m = null;
243 if (depFailAct != null) {
244 FailedDependencyAction fda =
245 FailedDependencyAction.fromString(depFailAct);
246 if (fda != null) {
247 m = fda.getDisplayName();
248 }
249 }
250 return m;
251 }
252
253 /**
254 * Gets the logs associated with this task's execution.
255 *
256 * @return array of log messages
257 */
258 public List<Message> getLogMessages() {
259 List<Message> formattedLogs = new ArrayList<Message>();
260 for (String aLog : logs) {
261 formattedLogs.add(Message.raw(aLog));
262 }
263 return Collections.unmodifiableList(formattedLogs);
264 }
265
266 /**
267 * Gets the email messages that will be used for notifications
268 * when the task completes.
269 *
270 * @return array of email addresses
271 */
272 public List<String> getCompletionNotificationEmailAddresses() {
273 return Collections.unmodifiableList(notifyComp);
274 }
275
276 /**
277 * Gets the email messages that will be used for notifications
278 * when the task encounters an error.
279 *
280 * @return array of email addresses
281 */
282 public List<String> getErrorNotificationEmailAddresses() {
283 return Collections.unmodifiableList(notifyErr);
284 }
285
286 /**
287 * Gets a user presentable string indicating the type of this task.
288 *
289 * @return Message type
290 */
291 public Message getType() {
292 Message type = Message.EMPTY;
293 if (className != null) {
294 type = mapClassToTypeName.get(className);
295 if (type == null) {
296 Task task = getTask();
297 if (task != null) {
298 try {
299 Method m = Task.class.getMethod("getDisplayName");
300 Object oName = m.invoke(task);
301 if (oName instanceof Message) {
302 mapClassToTypeName.put(className, (Message) oName);
303 type = (Message) oName;
304 }
305 } catch (Exception e) {
306 // ignore; this is best effort
307 }
308 }
309 }
310
311 // If we still can't get the type just resort
312 // to the class displayName
313 if (type == null) {
314 type = Message.raw(className);
315 }
316 }
317 return type;
318 }
319
320 /**
321 * Indicates whether or not this task supports a cancel operation.
322 *
323 * @return boolean where true means this task supports being canceled.
324 */
325 public boolean isCancelable() {
326 boolean cancelable = false;
327 TaskState state = getTaskState();
328 if (state != null) {
329 Task task = getTask();
330 cancelable = (TaskState.isPending(state) ||
331 (TaskState.isRunning(state) &&
332 task != null &&
333 task.isInterruptable()));
334 }
335 return cancelable;
336 }
337
338 /**
339 * Gets a mapping of attributes that are specific to the implementing
340 * task as opposed to the superior, or base, task.
341 *
342 * @return mapping of atribute field labels to lists of string values for
343 * each field.
344 */
345 public Map<Message, List<String>> getTaskSpecificAttributeValuePairs() {
346 return taskSpecificAttrValues;
347 }
348
349 /**
350 * Gets the task state.
351 *
352 * @return TaskState of task
353 */
354 public TaskState getTaskState() {
355 TaskState ts = null;
356 if (state != null) {
357 ts = TaskState.fromString(state);
358 }
359 return ts;
360 }
361
362 /**
363 * Indicates whether or not this task is done.
364 *
365 * @return boolean where true means this task is done
366 */
367 public boolean isDone() {
368 TaskState ts = getTaskState();
369 return ts != null && TaskState.isDone(ts);
370 }
371
372 private String getSingleStringValue(Entry entry, String attrName) {
373 List<Attribute> attrList = entry.getAttribute(attrName);
374 if (attrList != null && attrList.size() == 1) {
375 Set<AttributeValue> values = attrList.get(0).getValues();
376 if (values != null && values.size() == 1) {
377 return values.iterator().next().getStringValue();
378 }
379 }
380 return "";
381 }
382
383 private List<String> getMultiStringValue(Entry entry, String attrName) {
384 List<String> valuesList = new ArrayList<String>();
385 List<Attribute> attrList = entry.getAttribute(attrName);
386 if (attrList != null) {
387 for (Attribute attr : attrList) {
388 Set<AttributeValue> values = attr.getValues();
389 if (values != null) {
390 for (AttributeValue value : values) {
391 valuesList.add(value.getStringValue());
392 }
393 }
394 }
395 }
396 return valuesList;
397 }
398
399 private Message getAttributeDisplayName(String attrName) {
400 Message name = mapAttrToDisplayName.get(attrName);
401 if (name == null) {
402 Task task = getTask();
403 if (task != null) {
404 try {
405 Method m = Task.class.getMethod(
406 "getAttributeDisplayName", String.class);
407 Object o = m.invoke(task, attrName);
408 if (o != null && Message.class.isAssignableFrom(o.getClass())) {
409 name= (Message)o;
410 mapAttrToDisplayName.put(attrName, name);
411 }
412 } catch (Exception e) {
413 // ignore
414 }
415 }
416 }
417 if (name == null) {
418 name = Message.raw(attrName);
419 }
420 return name;
421 }
422
423 /**
424 * Formats a time string into a human friendly format.
425 * @param timeString the is human hostile
426 * @return string of time that is human friendly
427 */
428 private Message formatTimeString(String timeString) {
429 Message ret = Message.EMPTY;
430 if (timeString != null && timeString.length() > 0) {
431 try {
432 SimpleDateFormat dateFormat;
433 if (timeString.endsWith("Z")) {
434 dateFormat = new SimpleDateFormat(DATE_FORMAT_GMT_TIME);
435 dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
436 } else {
437 dateFormat = new SimpleDateFormat(DATE_FORMAT_COMPACT_LOCAL_TIME);
438 }
439 Date date = dateFormat.parse(timeString);
440 DateFormat df = DateFormat.getDateTimeInstance(
441 DateFormat.MEDIUM,
442 DateFormat.LONG);
443 String dateString = df.format(date);
444 ret = Message.raw(dateString);
445 } catch (ParseException pe){
446 ret = Message.raw(timeString);
447 }
448 }
449 return ret;
450 }
451
452 private Task getTask() {
453 if (task == null && className != null) {
454 try {
455 Class<?> clazz = Class.forName(className);
456 Object o = clazz.newInstance();
457 if (Task.class.isAssignableFrom(o.getClass())) {
458 this.task = (Task) o;
459 }
460 } catch (Exception e) {
461 // ignore; this is best effort
462 }
463 }
464 return task;
465 }
466
467 }