/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * docs/licenses/cddl.txt * or http://www.opensource.org/licenses/cddl1.php. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * docs/licenses/cddl.txt. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2018-2024 Ping Identity Corporation */ package com.unboundid.directory.sdk.examples; import java.io.File; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import com.unboundid.directory.sdk.ds.api.RecurringTask; import com.unboundid.directory.sdk.ds.api.Task; import com.unboundid.directory.sdk.ds.config.RecurringTaskConfig; import com.unboundid.directory.sdk.ds.types.DirectoryServerContext; import com.unboundid.ldap.sdk.LDAPException; import com.unboundid.ldap.sdk.ReadOnlyEntry; import com.unboundid.ldap.sdk.ResultCode; import com.unboundid.util.StaticUtils; import com.unboundid.util.args.ArgumentException; import com.unboundid.util.args.ArgumentParser; import com.unboundid.util.args.DNArgument; import com.unboundid.util.args.FileArgument; import com.unboundid.util.args.FilterArgument; import com.unboundid.util.args.StringArgument; /** * This class provides a simple example of a recurring task that will invoke an * internal search and write the results to a file on the server filesystem. * The search criteria will be specified using the following arguments: * <UL> * <LI>base-dn -- The base DN for the search to perform.</LI> * <LI>scope -- The scope for the search. The value should be one of the * following strings: base, one, sub, or subordinate-subtree.</LI> * <LI>filter -- The filter for the search.</LI> * <LI>attribute -- An attribute to request. This may be provided multiple * times to request multiple attributes.</LI> * <LI>output-file -- The path to the file to which the search results should * be written. A timestamp will be appended to the filename to ensure * that the output of each instance of the recurring task is written to * a separate file.</LI> * </UL> * Note that the actual work of honoring these arguments is performed by the * {@link ExampleTask}. This recurring task merely ensures that the appropriate * values are provided as arguments when the task is scheduled. */ public final class ExampleRecurringTask extends RecurringTask { /** * The name of the argument used to specify the search base DN. */ private static final String ARG_NAME_BASE_DN = "base-dn"; /** * The name of the argument used to specify the search scope. */ private static final String ARG_NAME_SCOPE = "scope"; /** * The name of the argument used to specify the filter. */ private static final String ARG_NAME_FILTER = "filter"; /** * The name of the argument used to specify the requested attributes. */ private static final String ARG_NAME_ATTR = "attribute"; /** * The name of the argument used to specify the path to the output file. */ private static final String ARG_NAME_OUTPUT_FILE = "output-file"; /** * The set of allowed search scope values. */ private static final Set<String> ALLOWED_SCOPES; static { final LinkedHashSet<String> scopeSet = new LinkedHashSet<>(4); scopeSet.add("base"); scopeSet.add("one"); scopeSet.add("sub"); scopeSet.add("subordinate-subtree"); ALLOWED_SCOPES = Collections.unmodifiableSet(scopeSet); } // The argument parser with the latest configuration for this extension. private volatile ArgumentParser parser; /** * Creates a new instance of this recurring task. All recurring task * implementations must include a default constructor, but any initialization * should generally be done in the {@link #initializeRecurringTask} method. */ public ExampleRecurringTask() { parser = null; } /** * Retrieves a human-readable name for this extension. * * @return A human-readable name for this extension. */ @Override() public String getExtensionName() { return "Example Recurring Task"; } /** * Retrieves a human-readable description for this extension. Each element * of the array that is returned will be considered a separate paragraph in * generated documentation. * * @return A human-readable description for this extension, or {@code null} * or an empty array if no description should be available. */ @Override() public String[] getExtensionDescription() { return new String[] { "This recurring task serves as an example that may be used to " + "demonstrate the process for creating a third-party recurring " + "task. It will create an " + ExampleTask.class.getName() + " instance that will perform an internal search (in which the " + "base DN, scope, filter, and requested attributes are provided as " + "task arguments) write the results to a file on the server " + "filesystem. The name of the output file will include a " + "timestamp to ensure that each task instance scheduled from this " + "recurring task has a unique filename." }; } /** * Updates the provided argument parser to define any configuration arguments * that may be used by this recurring task. The argument parser may also be * updated to define relationships between arguments (e.g., to specify * required, exclusive, or dependent argument sets). * * @param parser The argument parser to be updated with the configuration * arguments that may be used by this recurring task. * * @throws ArgumentException If a problem is encountered while updating the * provided argument parser. */ @Override() public void defineConfigArguments(final ArgumentParser parser) throws ArgumentException { // Add an argument that allows you to specify the search base DN. Character shortIdentifier = null; String longIdentifier = ARG_NAME_BASE_DN; boolean required = true; int maxOccurrences = 1; String placeholder = "{dn}"; String description = "The base DN to use for the search."; parser.addArgument(new DNArgument(shortIdentifier, longIdentifier, required, maxOccurrences, placeholder, description)); // Add an argument that allows you to specify the search scope. shortIdentifier = null; longIdentifier = ARG_NAME_SCOPE; required = true; maxOccurrences = 1; placeholder = "{base|one|sub|subordinate-subtree}"; description = "The scope to use for the search."; parser.addArgument(new StringArgument(shortIdentifier, longIdentifier, required, maxOccurrences, placeholder, description, ALLOWED_SCOPES)); // Add an argument that allows you to specify the search filter. shortIdentifier = null; longIdentifier = ARG_NAME_FILTER; required = true; maxOccurrences = 1; placeholder = "{filter}"; description = "The filter to use for the search."; parser.addArgument(new FilterArgument(shortIdentifier, longIdentifier, required, maxOccurrences, placeholder, description)); // Add an argument that allows you to specify the requested attributes. shortIdentifier = null; longIdentifier = ARG_NAME_ATTR; required = false; maxOccurrences = 0; // No limit placeholder = "{attr}"; description = "An attribute to include in matching entries. This " + "argument may be provided multiple times to request multiple " + "attributes. If no requested attributes are provided, then all " + "user attributes will be requested."; parser.addArgument(new StringArgument(shortIdentifier, longIdentifier, required, maxOccurrences, placeholder, description)); // Add an argument that allows you to specify the output file. shortIdentifier = null; longIdentifier = ARG_NAME_OUTPUT_FILE; required = true; maxOccurrences = 1; placeholder = "{path}"; description = "The output file to which the search results should be " + "written. Relative paths will be relative to the server root."; boolean fileMustExist = false; boolean parentMustExist = true; boolean mustBeFile = true; boolean mustBeDirectory = false; parser.addArgument(new FileArgument(shortIdentifier, longIdentifier, required, maxOccurrences, placeholder, description, fileMustExist, parentMustExist, mustBeFile, mustBeDirectory)); } /** * Initializes this recurring task. * * @param serverContext A handle to the server context for the server in * which this extension is running. * @param config The general configuration for this recurring task. * @param parser The argument parser which has been initialized from * the configuration for this recurring task. * * @throws LDAPException If a problem occurs while initializing this * recurring task. */ @Override() public void initializeRecurringTask( final DirectoryServerContext serverContext, final RecurringTaskConfig config, final ArgumentParser parser) throws LDAPException { serverContext.debugInfo("Beginning recurring task initialization"); this.parser = parser; } /** * Indicates whether the configuration contained in the provided argument * parser represents a valid configuration for this extension. * * @param config The general configuration for this identity * mapper. * @param parser The argument parser which has been initialized * with the proposed configuration. * @param unacceptableReasons A list that can be updated with reasons that * the proposed configuration is not acceptable. * * @return {@code true} if the proposed configuration is acceptable, or * {@code false} if not. */ @Override() public boolean isConfigurationAcceptable(final RecurringTaskConfig config, final ArgumentParser parser, final List<String> unacceptableReasons) { // No extended validation will be performed by default. return true; } /** * Attempts to apply the configuration contained in the provided argument * parser. * * @param config The general configuration for this identity * mapper. * @param parser The argument parser which has been * initialized with the new configuration. * @param adminActionsRequired A list that can be updated with information * about any administrative actions that may be * required before one or more of the * configuration changes will be applied. * @param messages A list that can be updated with information * about the result of applying the new * configuration. * * @return A result code that provides information about the result of * attempting to apply the configuration change. */ @Override() public ResultCode applyConfiguration(final RecurringTaskConfig config, final ArgumentParser parser, final List<String> adminActionsRequired, final List<String> messages) { // Just hold onto the new configuration. We won't look at it until we // actually create the task arguments. this.parser = parser; return ResultCode.SUCCESS; } /** * Performs any cleanup which may be necessary when this recurring task is * to be taken out of service. */ @Override() public void finalizeRecurringTask() { // No implementation is required. } /** * Retrieves the Java class that will be used to process instances of this * recurring task. * * @return The java class that will be used ot process instances of this * recurring task. It must not be {@code null}. */ @Override() public Class<? extends Task> getTaskJavaClass() { return ExampleTask.class; } /** * Retrieves the list of values that should be provided to the * ds-third-party-task-argument attribute for the task instance that is * created. * * @param scheduledStartTime The scheduled start time that will be used for * the task that is created. * @param lastTaskEntry The entry for the last instance of the * recurring task that was scheduled. It may be * {@code null} if the last instance is not * available (for example, because no instances of * the recurring task have yet been scheduled. * * @return The list of values that should be provided to the * ds-third-party-task-argument for the task instance that is * created. It may be {@code null} or empty if the task does not * require any arguments. */ @Override() public List<String> getTaskArguments(final ZonedDateTime scheduledStartTime, final ReadOnlyEntry lastTaskEntry) { final ArrayList<String> taskArguments = new ArrayList<>(10); // Get the base DN. final DNArgument baseArg = (DNArgument) parser.getNamedArgument(ARG_NAME_BASE_DN); taskArguments.add(ARG_NAME_BASE_DN + '=' + baseArg.getValue().toString()); // Get the scope. final StringArgument scopeArg = (StringArgument) parser.getNamedArgument(ARG_NAME_SCOPE); taskArguments.add(ARG_NAME_SCOPE + '=' + scopeArg.getValue()); // Get the filter. final FilterArgument filterArg = (FilterArgument) parser.getNamedArgument(ARG_NAME_FILTER); taskArguments.add(ARG_NAME_FILTER + '=' + filterArg.getValue().toString()); // Get the list of requested attributes. final StringArgument attrsArg = (StringArgument) parser.getNamedArgument(ARG_NAME_ATTR); if (attrsArg.isPresent()) { for (final String attr : attrsArg.getValues()) { taskArguments.add(ARG_NAME_ATTR + '=' + attr); } } // Get the output file. final FileArgument outputFileArg = (FileArgument) parser.getNamedArgument(ARG_NAME_OUTPUT_FILE); final String outputFilePath = outputFileArg.getValue().getAbsolutePath(); final String timestampedPath = outputFilePath + '.' + StaticUtils.encodeGeneralizedTime( scheduledStartTime.toInstant().toEpochMilli()); taskArguments.add(ARG_NAME_OUTPUT_FILE + '=' + timestampedPath); return taskArguments; } /** * {@inheritDoc} */ @Override() public Map<List<String>,String> getExamplesArgumentSets() { final LinkedHashMap<List<String>,String> exampleMap = new LinkedHashMap<>(1); exampleMap.put( Arrays.asList( ARG_NAME_BASE_DN + "=cn=monitor", ARG_NAME_SCOPE + "=sub", ARG_NAME_FILTER + "=(objectClass=*)", ARG_NAME_ATTR + "=*", ARG_NAME_ATTR + "=+", ARG_NAME_OUTPUT_FILE + "=logs" + File.separator + "monitor.ldif"), "Retrieve all of the server's monitor entries and write them to a " + "file named monitor.ldif.{timestamp} in the logs directory."); return Collections.unmodifiableMap(exampleMap); } }