/*
* 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
*
*
* Copyright 2018-2020 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);
}
}