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 }