/*
* The contents of this file are subject to the terms of the Common Development and
* Distribution License (the License). You may not use this file except in compliance with the
* License.
*
* You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
* specific language governing permission and limitations under the License.
*
* When distributing Covered Software, include this CDDL Header Notice in each file and include
* the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
* Header, with the fields enclosed by brackets [] replaced by your own identifying
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2006-2010 Sun Microsystems, Inc.
* Portions Copyright 2014-2016 ForgeRock AS.
*/
package org.opends.server.backends.task;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.forgerock.i18n.LocalizableMessage;
import org.forgerock.i18n.LocalizableMessageDescriptor.Arg1;
import org.forgerock.i18n.slf4j.LocalizedLogger;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.schema.AttributeType;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ServerContext;
import org.opends.server.types.Attribute;
import org.opends.server.types.Attributes;
import org.forgerock.opendj.ldap.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.Entry;
import org.opends.server.types.InitializationException;
import org.forgerock.opendj.ldap.RDN;
import static java.util.Calendar.*;
import static org.opends.messages.BackendMessages.*;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
/**
* This class defines a information about a recurring task, which will be used
* to repeatedly schedule tasks for processing.
*
* It also provides some static methods that allow to validate strings in
* crontab (5) format.
*/
public class RecurringTask
{
private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
/** The DN of the entry that actually defines this task. */
private final DN recurringTaskEntryDN;
/** The entry that actually defines this task. */
private final Entry recurringTaskEntry;
/** The unique ID for this recurring task. */
private final String recurringTaskID;
/**
* The fully-qualified name of the class that will be used to implement the
* class.
*/
private final String taskClassName;
/** Task instance. */
private Task task;
/** Task scheduler for this task. */
private final TaskScheduler taskScheduler;
/** Number of tokens in the task schedule tab. */
private static final int TASKTAB_NUM_TOKENS = 5;
/** Maximum year month days. */
static final int MONTH_LENGTH[]
= {31,28,31,30,31,30,31,31,30,31,30,31};
/** Maximum leap year month days. */
static final int LEAP_MONTH_LENGTH[]
= {31,29,31,30,31,30,31,31,30,31,30,31};
/** Task tab fields. */
private static enum TaskTab {MINUTE, HOUR, DAY, MONTH, WEEKDAY}
private static final int MINUTE_INDEX = 0;
private static final int HOUR_INDEX = 1;
private static final int DAY_INDEX = 2;
private static final int MONTH_INDEX = 3;
private static final int WEEKDAY_INDEX = 4;
/** Wildcard match pattern. */
private static final Pattern wildcardPattern = Pattern.compile("^\\*(?:/(\\d+))?");
/** Exact match pattern. */
private static final Pattern exactPattern = Pattern.compile("(\\d+)");
/** Range match pattern. */
private static final Pattern rangePattern = Pattern.compile("(\\d+)-(\\d+)(?:/(\\d+))?");
/** Boolean arrays holding task tab slots. */
private final boolean[] minutesArray;
private final boolean[] hoursArray;
private final boolean[] daysArray;
private final boolean[] monthArray;
private final boolean[] weekdayArray;
private final ServerContext serverContext;
/**
* Creates a new recurring task based on the information in the provided
* entry.
*
* @param serverContext
* The server context.
*
* @param taskScheduler A reference to the task scheduler that may be
* used to schedule new tasks.
* @param recurringTaskEntry The entry containing the information to use to
* define the task to process.
*
* @throws DirectoryException If the provided entry does not contain a valid
* recurring task definition.
*/
public RecurringTask(ServerContext serverContext, TaskScheduler taskScheduler, Entry recurringTaskEntry)
throws DirectoryException
{
this.serverContext = serverContext;
this.taskScheduler = taskScheduler;
this.recurringTaskEntry = recurringTaskEntry;
this.recurringTaskEntryDN = recurringTaskEntry.getName();
// Get the recurring task ID from the entry. If there isn't one, then fail.
Attribute attr = getSingleAttribute(recurringTaskEntry, ATTR_RECURRING_TASK_ID,
ERR_RECURRINGTASK_NO_ID_ATTRIBUTE, ERR_RECURRINGTASK_MULTIPLE_ID_TYPES, ERR_RECURRINGTASK_NO_ID);
recurringTaskID = getSingleAttributeValue(attr,
ResultCode.OBJECTCLASS_VIOLATION, ERR_RECURRINGTASK_MULTIPLE_ID_VALUES, ATTR_RECURRING_TASK_ID);
// Get the schedule for this task.
attr = getSingleAttribute(recurringTaskEntry, ATTR_RECURRING_TASK_SCHEDULE,ERR_RECURRINGTASK_NO_SCHEDULE_ATTRIBUTE,
ERR_RECURRINGTASK_MULTIPLE_SCHEDULE_TYPES, ERR_RECURRINGTASK_NO_SCHEDULE_VALUES);
String taskScheduleTab = getSingleAttributeValue(attr,
ResultCode.CONSTRAINT_VIOLATION, ERR_RECURRINGTASK_MULTIPLE_SCHEDULE_VALUES, ATTR_RECURRING_TASK_SCHEDULE);
boolean[][] taskArrays = new boolean[][]{null, null, null, null, null};
parseTaskTab(taskScheduleTab, taskArrays, true);
minutesArray = taskArrays[MINUTE_INDEX];
hoursArray = taskArrays[HOUR_INDEX];
daysArray = taskArrays[DAY_INDEX];
monthArray = taskArrays[MONTH_INDEX];
weekdayArray = taskArrays[WEEKDAY_INDEX];
// Get the class name from the entry. If there isn't one, then fail.
attr = getSingleAttribute(recurringTaskEntry, ATTR_TASK_CLASS, ERR_TASKSCHED_NO_CLASS_ATTRIBUTE,
ERR_TASKSCHED_MULTIPLE_CLASS_TYPES, ERR_TASKSCHED_NO_CLASS_VALUES);
taskClassName = getSingleAttributeValue(attr,
ResultCode.CONSTRAINT_VIOLATION, ERR_TASKSCHED_MULTIPLE_CLASS_VALUES, ATTR_TASK_CLASS);
// Make sure that the specified class can be loaded.
Class> taskClass;
try
{
taskClass = DirectoryServer.loadClass(taskClassName);
}
catch (Exception e)
{
logger.traceException(e);
LocalizableMessage message = ERR_RECURRINGTASK_CANNOT_LOAD_CLASS.
get(taskClassName, ATTR_TASK_CLASS, getExceptionMessage(e));
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, e);
}
// Make sure that the specified class can be instantiated as a task.
try
{
task = (Task) taskClass.newInstance();
}
catch (Exception e)
{
logger.traceException(e);
LocalizableMessage message = ERR_RECURRINGTASK_CANNOT_INSTANTIATE_CLASS_AS_TASK.get(
taskClassName, Task.class.getName());
throw new DirectoryException(ResultCode.CONSTRAINT_VIOLATION, message, e);
}
// Make sure that we can initialize the task with the information in the
// provided entry.
try
{
task.initializeTaskInternal(serverContext, taskScheduler, recurringTaskEntry);
}
catch (InitializationException ie)
{
logger.traceException(ie);
LocalizableMessage message = ERR_RECURRINGTASK_CANNOT_INITIALIZE_INTERNAL.get( taskClassName, ie.getMessage());
throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, ie);
}
task.initializeTask();
}
private String getSingleAttributeValue(Attribute attr, ResultCode erorrRc, Arg1