001/* 002 * Copyright 2008-2024 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright 2008-2024 Ping Identity Corporation 007 * 008 * Licensed under the Apache License, Version 2.0 (the "License"); 009 * you may not use this file except in compliance with the License. 010 * You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, software 015 * distributed under the License is distributed on an "AS IS" BASIS, 016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 017 * See the License for the specific language governing permissions and 018 * limitations under the License. 019 */ 020/* 021 * Copyright (C) 2008-2024 Ping Identity Corporation 022 * 023 * This program is free software; you can redistribute it and/or modify 024 * it under the terms of the GNU General Public License (GPLv2 only) 025 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 026 * as published by the Free Software Foundation. 027 * 028 * This program is distributed in the hope that it will be useful, 029 * but WITHOUT ANY WARRANTY; without even the implied warranty of 030 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 031 * GNU General Public License for more details. 032 * 033 * You should have received a copy of the GNU General Public License 034 * along with this program; if not, see <http://www.gnu.org/licenses>. 035 */ 036package com.unboundid.ldap.sdk.unboundidds.tasks; 037 038 039 040import java.util.ArrayList; 041import java.util.Arrays; 042import java.util.Collections; 043import java.util.Date; 044import java.util.LinkedHashMap; 045import java.util.List; 046import java.util.Map; 047 048import com.unboundid.ldap.sdk.Attribute; 049import com.unboundid.ldap.sdk.Entry; 050import com.unboundid.util.NotMutable; 051import com.unboundid.util.NotNull; 052import com.unboundid.util.Nullable; 053import com.unboundid.util.StaticUtils; 054import com.unboundid.util.ThreadSafety; 055import com.unboundid.util.ThreadSafetyLevel; 056import com.unboundid.util.Validator; 057 058import static com.unboundid.ldap.sdk.unboundidds.tasks.TaskMessages.*; 059 060 061 062/** 063 * This class defines a Directory Server task that can be used to invoke a task 064 * written as a Groovy script using the UnboundID Server SDK. 065 * <BR> 066 * <BLOCKQUOTE> 067 * <B>NOTE:</B> This class, and other classes within the 068 * {@code com.unboundid.ldap.sdk.unboundidds} package structure, are only 069 * supported for use against Ping Identity, UnboundID, and 070 * Nokia/Alcatel-Lucent 8661 server products. These classes provide support 071 * for proprietary functionality or for external specifications that are not 072 * considered stable or mature enough to be guaranteed to work in an 073 * interoperable way with other types of LDAP servers. 074 * </BLOCKQUOTE> 075 * <BR> 076 * The properties that are available for use with this type of task include: 077 * <UL> 078 * <LI>The fully-qualified name of the Groovy class providing the logic for 079 * the scripted task. This must be provided.</LI> 080 * <LI>A list of the arguments to use for the task.</LI> 081 * </UL> 082 */ 083@NotMutable() 084@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 085public final class GroovyScriptedTask 086 extends Task 087{ 088 /** 089 * The fully-qualified name of the Java class that is used for the core 090 * Groovy-scripted task. 091 */ 092 @NotNull static final String GROOVY_SCRIPTED_TASK_CLASS = 093 "com.unboundid.directory.sdk.extensions.GroovyScriptedTask"; 094 095 096 097 /** 098 * The name of the attribute used to specify the fully-qualified name of the 099 * Groovy class providing the logic for the scripted task. 100 */ 101 @NotNull private static final String ATTR_GROOVY_SCRIPTED_TASK_CLASS = 102 "ds-scripted-task-class"; 103 104 105 106 /** 107 * The name of the attribute used to provide arguments to the script. 108 */ 109 @NotNull private static final String ATTR_GROOVY_SCRIPTED_TASK_ARGUMENT = 110 "ds-scripted-task-argument"; 111 112 113 114 /** 115 * The name of the object class used in Groovy-scripted task entries. 116 */ 117 @NotNull private static final String OC_GROOVY_SCRIPTED_TASK = 118 "ds-groovy-scripted-task"; 119 120 121 122 /** 123 * The task property that will be used for the task class. 124 */ 125 @NotNull static final TaskProperty PROPERTY_TASK_CLASS = 126 new TaskProperty(ATTR_GROOVY_SCRIPTED_TASK_CLASS, 127 INFO_DISPLAY_NAME_GROOVY_SCRIPTED_TASK_CLASS.get(), 128 INFO_DESCRIPTION_GROOVY_SCRIPTED_TASK_CLASS.get(), String.class, true, 129 false, false); 130 131 132 133 /** 134 * The task property that will be used for the task arguments. 135 */ 136 @NotNull static final TaskProperty PROPERTY_TASK_ARG = 137 new TaskProperty(ATTR_GROOVY_SCRIPTED_TASK_ARGUMENT, 138 INFO_DISPLAY_NAME_GROOVY_SCRIPTED_TASK_ARG.get(), 139 INFO_DESCRIPTION_GROOVY_SCRIPTED_TASK_ARG.get(), String.class, false, 140 true, false); 141 142 143 144 /** 145 * The serial version UID for this serializable class. 146 */ 147 private static final long serialVersionUID = -1354970323227263273L; 148 149 150 151 // A list of the arguments for the task. 152 @NotNull private final List<String> taskArguments; 153 154 // The name of the Groovy class providing the logic for the scripted task. 155 @NotNull private final String taskClassName; 156 157 158 159 /** 160 * Creates a new uninitialized Groovy-scripted task instance which should only 161 * be used for obtaining general information about this task, including the 162 * task name, description, and supported properties. Attempts to use a task 163 * created with this constructor for any other reason will likely fail. 164 */ 165 public GroovyScriptedTask() 166 { 167 taskArguments = null; 168 taskClassName = null; 169 } 170 171 172 173 /** 174 * Creates a new Groovy-scripted task with the provided information. 175 * 176 * @param taskID The task ID to use for this task. If it is 177 * {@code null} then a UUID will be generated for use 178 * as the task ID. 179 * @param taskClassName The fully-qualified name of the Groovy class 180 * providing the logic for the task. It must not be 181 * {@code null}. 182 * @param taskArguments A list of the arguments for the task, in the form 183 * name=value. It may be {@code null} or empty if 184 * there should not be any arguments. 185 */ 186 public GroovyScriptedTask(@Nullable final String taskID, 187 @NotNull final String taskClassName, 188 @Nullable final List<String> taskArguments) 189 { 190 this(taskID, taskClassName, taskArguments, null, null, null, null, null); 191 } 192 193 194 195 /** 196 * Creates a new Groovy-scripted task with the provided information. 197 * 198 * @param taskID The task ID to use for this task. If it is 199 * {@code null} then a UUID will be generated 200 * for use as the task ID. 201 * @param taskClassName The fully-qualified name of the Groovy 202 * class providing the logic for the task. It 203 * must not be {@code null}. 204 * @param taskArguments A list of the arguments for the task, in 205 * the form name=value. It may be 206 * {@code null} or empty if there should not 207 * be any arguments. 208 * @param scheduledStartTime The time that this task should start 209 * running. 210 * @param dependencyIDs The list of task IDs that will be required 211 * to complete before this task will be 212 * eligible to start. 213 * @param failedDependencyAction Indicates what action should be taken if 214 * any of the dependencies for this task do 215 * not complete successfully. 216 * @param notifyOnCompletion The list of e-mail addresses of individuals 217 * that should be notified when this task 218 * completes. 219 * @param notifyOnError The list of e-mail addresses of individuals 220 * that should be notified if this task does 221 * not complete successfully. 222 */ 223 public GroovyScriptedTask(@Nullable final String taskID, 224 @NotNull final String taskClassName, 225 @Nullable final List<String> taskArguments, 226 @Nullable final Date scheduledStartTime, 227 @Nullable final List<String> dependencyIDs, 228 @Nullable final FailedDependencyAction failedDependencyAction, 229 @Nullable final List<String> notifyOnCompletion, 230 @Nullable final List<String> notifyOnError) 231 { 232 this(taskID, taskClassName, taskArguments, scheduledStartTime, 233 dependencyIDs, failedDependencyAction, null, notifyOnCompletion, 234 null, notifyOnError, null, null, null); 235 } 236 237 238 239 /** 240 * Creates a new Groovy-scripted task with the provided information. 241 * 242 * @param taskID The task ID to use for this task. If it is 243 * {@code null} then a UUID will be generated 244 * for use as the task ID. 245 * @param taskClassName The fully-qualified name of the Groovy 246 * class providing the logic for the task. It 247 * must not be {@code null}. 248 * @param taskArguments A list of the arguments for the task, in 249 * the form name=value. It may be 250 * {@code null} or empty if there should not 251 * be any arguments. 252 * @param scheduledStartTime The time that this task should start 253 * running. 254 * @param dependencyIDs The list of task IDs that will be required 255 * to complete before this task will be 256 * eligible to start. 257 * @param failedDependencyAction Indicates what action should be taken if 258 * any of the dependencies for this task do 259 * not complete successfully. 260 * @param notifyOnStart The list of e-mail addresses of individuals 261 * that should be notified when this task 262 * starts running. 263 * @param notifyOnCompletion The list of e-mail addresses of individuals 264 * that should be notified when this task 265 * completes. 266 * @param notifyOnSuccess The list of e-mail addresses of individuals 267 * that should be notified if this task 268 * completes successfully. 269 * @param notifyOnError The list of e-mail addresses of individuals 270 * that should be notified if this task does 271 * not complete successfully. 272 * @param alertOnStart Indicates whether the server should send an 273 * alert notification when this task starts. 274 * @param alertOnSuccess Indicates whether the server should send an 275 * alert notification if this task completes 276 * successfully. 277 * @param alertOnError Indicates whether the server should send an 278 * alert notification if this task fails to 279 * complete successfully. 280 */ 281 public GroovyScriptedTask(@Nullable final String taskID, 282 @NotNull final String taskClassName, 283 @Nullable final List<String> taskArguments, 284 @Nullable final Date scheduledStartTime, 285 @Nullable final List<String> dependencyIDs, 286 @Nullable final FailedDependencyAction failedDependencyAction, 287 @Nullable final List<String> notifyOnStart, 288 @Nullable final List<String> notifyOnCompletion, 289 @Nullable final List<String> notifyOnSuccess, 290 @Nullable final List<String> notifyOnError, 291 @Nullable final Boolean alertOnStart, 292 @Nullable final Boolean alertOnSuccess, 293 @Nullable final Boolean alertOnError) 294 { 295 super(taskID, GROOVY_SCRIPTED_TASK_CLASS, scheduledStartTime, 296 dependencyIDs, failedDependencyAction, notifyOnStart, 297 notifyOnCompletion, notifyOnSuccess, notifyOnError, alertOnStart, 298 alertOnSuccess, alertOnError); 299 300 Validator.ensureNotNull(taskClassName); 301 302 this.taskClassName = taskClassName; 303 304 if (taskArguments == null) 305 { 306 this.taskArguments = Collections.emptyList(); 307 } 308 else 309 { 310 this.taskArguments = Collections.unmodifiableList(taskArguments); 311 } 312 } 313 314 315 316 /** 317 * Creates a new Groovy-scripted task from the provided entry. 318 * 319 * @param entry The entry to use to create this Groovy-scripted task. 320 * 321 * @throws TaskException If the provided entry cannot be parsed as a 322 * Groovy-scripted task entry. 323 */ 324 public GroovyScriptedTask(@NotNull final Entry entry) 325 throws TaskException 326 { 327 super(entry); 328 329 330 // Get the task class name. It must be present. 331 taskClassName = entry.getAttributeValue(ATTR_GROOVY_SCRIPTED_TASK_CLASS); 332 if (taskClassName == null) 333 { 334 throw new TaskException(ERR_GROOVY_SCRIPTED_TASK_NO_CLASS.get( 335 getTaskEntryDN())); 336 } 337 338 339 // Get the task arguments. It may be absent. 340 final String[] args = 341 entry.getAttributeValues(ATTR_GROOVY_SCRIPTED_TASK_ARGUMENT); 342 if ((args == null) || (args.length == 0)) 343 { 344 taskArguments = Collections.emptyList(); 345 } 346 else 347 { 348 taskArguments = Collections.unmodifiableList(Arrays.asList(args)); 349 } 350 } 351 352 353 354 /** 355 * Creates a new Groovy-scripted task from the provided set of task 356 * properties. 357 * 358 * @param properties The set of task properties and their corresponding 359 * values to use for the task. It must not be 360 * {@code null}. 361 * 362 * @throws TaskException If the provided set of properties cannot be used to 363 * create a valid Groovy-scripted task. 364 */ 365 public GroovyScriptedTask( 366 @NotNull final Map<TaskProperty,List<Object>> properties) 367 throws TaskException 368 { 369 super(GROOVY_SCRIPTED_TASK_CLASS, properties); 370 371 String className = null; 372 String[] args = null; 373 for (final Map.Entry<TaskProperty,List<Object>> entry : 374 properties.entrySet()) 375 { 376 final TaskProperty p = entry.getKey(); 377 final String attrName = p.getAttributeName(); 378 final List<Object> values = entry.getValue(); 379 380 if (attrName.equalsIgnoreCase(ATTR_GROOVY_SCRIPTED_TASK_CLASS)) 381 { 382 className = parseString(p, values, null); 383 } 384 else if (attrName.equalsIgnoreCase(ATTR_GROOVY_SCRIPTED_TASK_ARGUMENT)) 385 { 386 args = parseStrings(p, values, null); 387 } 388 } 389 390 if (className == null) 391 { 392 throw new TaskException(ERR_GROOVY_SCRIPTED_TASK_NO_CLASS.get( 393 getTaskEntryDN())); 394 } 395 396 taskClassName = className; 397 398 if (args == null) 399 { 400 taskArguments = Collections.emptyList(); 401 } 402 else 403 { 404 taskArguments = Collections.unmodifiableList(Arrays.asList(args)); 405 } 406 } 407 408 409 410 /** 411 * {@inheritDoc} 412 */ 413 @Override() 414 @NotNull() 415 public String getTaskName() 416 { 417 return INFO_TASK_NAME_GROOVY_SCRIPTED_TASK.get(); 418 } 419 420 421 422 /** 423 * {@inheritDoc} 424 */ 425 @Override() 426 @NotNull() 427 public String getTaskDescription() 428 { 429 return INFO_TASK_DESCRIPTION_GROOVY_SCRIPTED_TASK.get(); 430 } 431 432 433 434 /** 435 * Retrieves the fully-qualified name of the Groovy class providing the logic 436 * for the scripted task. 437 * 438 * @return The fully-qualified name of the Groovy class providing the logic 439 * for the scripted task. 440 */ 441 @NotNull() 442 public String getGroovyScriptedTaskClassName() 443 { 444 return taskClassName; 445 } 446 447 448 449 /** 450 * Retrieves a list of the arguments to provide to the Groovy-scripted task. 451 * 452 * @return A list of the arguments to provide to the Groovy-scripted task, or 453 * an empty list if there are no arguments. 454 */ 455 @NotNull() 456 public List<String> getGroovyScriptedTaskArguments() 457 { 458 return taskArguments; 459 } 460 461 462 463 /** 464 * {@inheritDoc} 465 */ 466 @Override() 467 @NotNull() 468 protected List<String> getAdditionalObjectClasses() 469 { 470 return Collections.singletonList(OC_GROOVY_SCRIPTED_TASK); 471 } 472 473 474 475 /** 476 * {@inheritDoc} 477 */ 478 @Override() 479 @NotNull() 480 protected List<Attribute> getAdditionalAttributes() 481 { 482 final ArrayList<Attribute> attrList = new ArrayList<>(2); 483 attrList.add(new Attribute(ATTR_GROOVY_SCRIPTED_TASK_CLASS, taskClassName)); 484 485 if (! taskArguments.isEmpty()) 486 { 487 attrList.add(new Attribute(ATTR_GROOVY_SCRIPTED_TASK_ARGUMENT, 488 taskArguments)); 489 } 490 491 return attrList; 492 } 493 494 495 496 /** 497 * {@inheritDoc} 498 */ 499 @Override() 500 @NotNull() 501 public List<TaskProperty> getTaskSpecificProperties() 502 { 503 return Collections.unmodifiableList(Arrays.asList( 504 PROPERTY_TASK_CLASS, 505 PROPERTY_TASK_ARG)); 506 } 507 508 509 510 /** 511 * {@inheritDoc} 512 */ 513 @Override() 514 @NotNull() 515 public Map<TaskProperty,List<Object>> getTaskPropertyValues() 516 { 517 final LinkedHashMap<TaskProperty,List<Object>> props = 518 new LinkedHashMap<>(StaticUtils.computeMapCapacity(2)); 519 520 props.put(PROPERTY_TASK_CLASS, 521 Collections.<Object>singletonList(taskClassName)); 522 523 props.put(PROPERTY_TASK_ARG, 524 Collections.<Object>unmodifiableList(taskArguments)); 525 526 props.putAll(super.getTaskPropertyValues()); 527 return Collections.unmodifiableMap(props); 528 } 529}