| | |
| | | import org.forgerock.i18n.slf4j.LocalizedLogger; |
| | | import org.forgerock.opendj.config.server.ConfigChangeResult; |
| | | import org.forgerock.opendj.config.server.ConfigException; |
| | | import org.forgerock.opendj.config.server.ConfigurationChangeListener; |
| | | import org.forgerock.opendj.ldap.ByteString; |
| | | import org.forgerock.opendj.ldap.ConditionResult; |
| | | import org.forgerock.opendj.ldap.DN; |
| | |
| | | import org.forgerock.opendj.ldap.ResultCode; |
| | | import org.forgerock.opendj.ldap.SearchScope; |
| | | import org.forgerock.opendj.ldap.schema.AttributeType; |
| | | import org.forgerock.util.Reject; |
| | | import org.forgerock.opendj.config.server.ConfigurationChangeListener; |
| | | import org.forgerock.opendj.server.config.server.TaskBackendCfg; |
| | | import org.forgerock.util.Reject; |
| | | import org.opends.server.api.Backend; |
| | | import org.opends.server.api.Backupable; |
| | | import org.opends.server.core.AddOperation; |
| | |
| | | extends Backend<TaskBackendCfg> |
| | | implements ConfigurationChangeListener<TaskBackendCfg>, Backupable |
| | | { |
| | | |
| | | private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); |
| | | |
| | | |
| | | |
| | | /** The current configuration state. */ |
| | | private TaskBackendCfg currentConfig; |
| | | |
| | | /** The DN of the configuration entry for this backend. */ |
| | | private DN configEntryDN; |
| | | |
| | | /** |
| | | * The DN of the entry that will serve as the parent for all recurring task |
| | | * entries. |
| | | */ |
| | | /** The DN of the entry that will serve as the parent for all recurring task entries. */ |
| | | private DN recurringTaskParentDN; |
| | | |
| | | /** |
| | | * The DN of the entry that will serve as the parent for all scheduled task |
| | | * entries. |
| | | */ |
| | | /** The DN of the entry that will serve as the parent for all scheduled task entries. */ |
| | | private DN scheduledTaskParentDN; |
| | | |
| | | /** The DN of the entry that will serve as the root for all task entries. */ |
| | | private DN taskRootDN; |
| | | |
| | |
| | | |
| | | /** The path to the task backing file. */ |
| | | private String taskBackingFile; |
| | | |
| | | /** |
| | | * The task scheduler that will be responsible for actually invoking scheduled |
| | | * tasks. |
| | | */ |
| | | /** The task scheduler that will be responsible for actually invoking scheduled tasks. */ |
| | | private TaskScheduler taskScheduler; |
| | | |
| | | private ServerContext serverContext; |
| | |
| | | // Perform all initialization in initializeBackend. |
| | | } |
| | | |
| | | |
| | | |
| | | @Override |
| | | public void configureBackend(TaskBackendCfg cfg, ServerContext serverContext) throws ConfigException |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | // Get the retention time that will be used to determine how long task |
| | | // information stays around once the associated task is completed. |
| | | retentionTime = cfg.getTaskRetentionTime(); |
| | | |
| | | |
| | | // Get the notification sender address. |
| | | notificationSenderAddress = cfg.getNotificationSenderAddress(); |
| | | if (notificationSenderAddress == null) |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | // Get the path to the task data backing file. |
| | | taskBackingFile = cfg.getTaskBackingFile(); |
| | | |
| | | currentConfig = cfg; |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void openBackend() |
| | | throws ConfigException, InitializationException |
| | |
| | | taskScheduler = new TaskScheduler(serverContext, this); |
| | | taskScheduler.start(); |
| | | |
| | | |
| | | // Register with the Directory Server as a configurable component. |
| | | currentConfig.addTaskChangeListener(this); |
| | | |
| | | |
| | | // Register the task base as a private suffix. |
| | | try |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void closeBackend() |
| | | { |
| | |
| | | return -1; |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public boolean isIndexed(AttributeType attributeType, IndexType indexType) |
| | | { |
| | |
| | | return true; |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public ConditionResult hasSubordinates(DN entryDN) |
| | | throws DirectoryException |
| | |
| | | return ConditionResult.valueOf(ret != 0); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public long getNumberOfEntriesInBaseDN(DN baseDN) throws DirectoryException { |
| | | checkNotNull(baseDN, "baseDN must not be null"); |
| | | return numSubordinates(baseDN, true) + 1; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public long getNumberOfChildren(DN parentDN) throws DirectoryException { |
| | | checkNotNull(parentDN, "parentDN must not be null"); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Entry getEntry(DN entryDN) |
| | | throws DirectoryException |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void addEntry(Entry entry, AddOperation addOperation) |
| | | throws DirectoryException |
| | |
| | | throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void deleteEntry(DN entryDN, DeleteOperation deleteOperation) |
| | | throws DirectoryException |
| | |
| | | throw new DirectoryException(ResultCode.NO_SUCH_OBJECT, message); |
| | | } |
| | | |
| | | |
| | | // Look at the state of the task. We will allow pending and completed |
| | | // tasks to be removed, but not running tasks. |
| | | TaskState state = t.getTaskState(); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void replaceEntry(Entry oldEntry, Entry newEntry, |
| | | ModifyOperation modifyOperation) throws DirectoryException |
| | |
| | | LocalizableMessage message = INFO_TASKBE_RUNNING_TASK_CANCELLED.get(); |
| | | t.interruptTask(TaskState.STOPPED_BY_ADMINISTRATOR, message); |
| | | } |
| | | return; |
| | | return; |
| | | } |
| | | else |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Helper to determine if requested modifications are acceptable. |
| | | * @param modifyOperation associated with requested modifications. |
| | |
| | | return true; |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void renameEntry(DN currentDN, Entry entry, |
| | | ModifyDNOperation modifyDNOperation) |
| | |
| | | ERR_BACKEND_MODIFY_DN_NOT_SUPPORTED.get(currentDN, getBackendID())); |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void search(SearchOperation searchOperation) |
| | | throws DirectoryException, CanceledOperationException { |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | if (searchRoot) |
| | | { |
| | | Entry e = taskScheduler.getTaskRootEntry(); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | if (searchScheduledParent) |
| | | { |
| | | Entry e = taskScheduler.getScheduledTaskParentEntry(); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | if (searchScheduledTasks |
| | | && !taskScheduler.searchScheduledTasks(searchOperation)) |
| | | { |
| | | return; |
| | | } |
| | | |
| | | |
| | | if (searchRecurringParent) |
| | | { |
| | | Entry e = taskScheduler.getRecurringTaskParentEntry(); |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | if (searchRecurringTasks |
| | | && !taskScheduler.searchRecurringTasks(searchOperation)) |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Set<String> getSupportedControls() |
| | | { |
| | | return Collections.emptySet(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Set<String> getSupportedFeatures() |
| | | { |
| | | return Collections.emptySet(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public boolean supports(BackendOperation backendOperation) |
| | | { |
| | |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void exportLDIF(LDIFExportConfig exportConfig) |
| | | throws DirectoryException |
| | | public void exportLDIF(LDIFExportConfig exportConfig) throws DirectoryException |
| | | { |
| | | File taskFile = getFileForPath(taskBackingFile); |
| | | |
| | | // Read from. |
| | | LDIFReader ldifReader; |
| | | try |
| | | try (LDIFReader ldifReader = newLDIFReader(taskFile); |
| | | LDIFWriter ldifWriter = newLDIFWriter(exportConfig)) |
| | | { |
| | | ldifReader = new LDIFReader(new LDIFImportConfig(taskFile.getPath())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | LocalizableMessage message = ERR_TASKS_CANNOT_EXPORT_TO_FILE.get(e); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, e); |
| | | } |
| | | |
| | | // Write to. |
| | | LDIFWriter ldifWriter; |
| | | try |
| | | { |
| | | ldifWriter = new LDIFWriter(exportConfig); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | |
| | | LocalizableMessage message = ERR_TASKS_CANNOT_EXPORT_TO_FILE.get( |
| | | stackTraceToSingleLineString(e)); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), |
| | | message); |
| | | } |
| | | |
| | | // Copy record by record. |
| | | try |
| | | { |
| | | // Copy record by record. |
| | | while (true) |
| | | { |
| | | Entry e = null; |
| | |
| | | } |
| | | catch (LDIFException le) |
| | | { |
| | | if (! le.canContinueReading()) |
| | | if (!le.canContinueReading()) |
| | | { |
| | | LocalizableMessage message = ERR_TASKS_CANNOT_EXPORT_TO_FILE.get(e); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, le); |
| | |
| | | { |
| | | logger.traceException(e); |
| | | } |
| | | finally |
| | | } |
| | | |
| | | private LDIFReader newLDIFReader(File taskFile) throws DirectoryException |
| | | { |
| | | try |
| | | { |
| | | close(ldifWriter, ldifReader); |
| | | return new LDIFReader(new LDIFImportConfig(taskFile.getPath())); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | LocalizableMessage message = ERR_TASKS_CANNOT_EXPORT_TO_FILE.get(e); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message, e); |
| | | } |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | private LDIFWriter newLDIFWriter(LDIFExportConfig exportConfig) throws DirectoryException |
| | | { |
| | | try |
| | | { |
| | | return new LDIFWriter(exportConfig); |
| | | } |
| | | catch (Exception e) |
| | | { |
| | | logger.traceException(e); |
| | | LocalizableMessage message = ERR_TASKS_CANNOT_EXPORT_TO_FILE.get(stackTraceToSingleLineString(e)); |
| | | throw new DirectoryException(DirectoryServer.getServerErrorResultCode(), message); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public LDIFImportResult importLDIF(LDIFImportConfig importConfig, ServerContext sContext) throws DirectoryException |
| | | { |
| | |
| | | ERR_BACKEND_IMPORT_NOT_SUPPORTED.get(getBackendID())); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void createBackup(BackupConfig backupConfig) throws DirectoryException |
| | | { |
| | | new BackupManager(getBackendID()).createBackup(this, backupConfig); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void removeBackup(BackupDirectory backupDirectory, String backupID) throws DirectoryException |
| | | { |
| | | new BackupManager(getBackendID()).removeBackup(backupDirectory, backupID); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void restoreBackup(RestoreConfig restoreConfig) throws DirectoryException |
| | | { |
| | | new BackupManager(getBackendID()).restoreBackup(this, restoreConfig); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public boolean isConfigurationAcceptable(TaskBackendCfg config, |
| | | List<LocalizableMessage> unacceptableReasons, |
| | |
| | | return isConfigAcceptable(config, unacceptableReasons, null); |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public boolean isConfigurationChangeAcceptable(TaskBackendCfg configEntry, |
| | | List<LocalizableMessage> unacceptableReasons) |
| | |
| | | taskBackingFile); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Indicates whether the provided configuration is acceptable for this task |
| | | * backend. |
| | |
| | | { |
| | | boolean configIsAcceptable = true; |
| | | |
| | | |
| | | try |
| | | { |
| | | String tmpBackingFile = config.getTaskBackingFile(); |
| | |
| | | return configIsAcceptable; |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public ConfigChangeResult applyConfigurationChange(TaskBackendCfg configEntry) |
| | | { |
| | | final ConfigChangeResult ccr = new ConfigChangeResult(); |
| | | |
| | | |
| | | String tmpBackingFile = taskBackingFile; |
| | | try |
| | | { |
| | |
| | | ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); |
| | | } |
| | | |
| | | |
| | | long tmpRetentionTime = configEntry.getTaskRetentionTime(); |
| | | |
| | | |
| | | if (ccr.getResultCode() == ResultCode.SUCCESS) |
| | | { |
| | | // Everything looks OK, so apply the changes. |
| | |
| | | ccr.addMessage(INFO_TASKBE_UPDATED_RETENTION_TIME.get(retentionTime)); |
| | | } |
| | | |
| | | |
| | | if (! taskBackingFile.equals(tmpBackingFile)) |
| | | { |
| | | taskBackingFile = tmpBackingFile; |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | String tmpNotificationAddress = configEntry.getNotificationSenderAddress(); |
| | | if (tmpNotificationAddress == null) |
| | | { |
| | |
| | | } |
| | | notificationSenderAddress = tmpNotificationAddress; |
| | | |
| | | |
| | | currentConfig = configEntry; |
| | | return ccr; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the DN of the configuration entry for this task backend. |
| | | * |
| | |
| | | return configEntryDN; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the path to the backing file that will hold the scheduled and |
| | | * recurring task definitions. |
| | |
| | | return f.getPath(); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the sender address that should be used for e-mail notifications |
| | | * of task completion. |
| | |
| | | return notificationSenderAddress; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the length of time in seconds that information for a task should |
| | | * be retained after processing on it has completed. |
| | |
| | | return retentionTime; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the DN of the entry that is the root for all task information in |
| | | * the Directory Server. |
| | |
| | | return taskRootDN; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the DN of the entry that is the immediate parent for all |
| | | * recurring task information in the Directory Server. |
| | |
| | | return recurringTaskParentDN; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the DN of the entry that is the immediate parent for all |
| | | * scheduled task information in the Directory Server. |
| | |
| | | return scheduledTaskParentDN; |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the scheduled task for the entry with the provided DN. |
| | | * |
| | |
| | | return taskScheduler.getScheduledTask(taskEntryDN); |
| | | } |
| | | |
| | | |
| | | |
| | | /** |
| | | * Retrieves the recurring task for the entry with the provided DN. |
| | | * |
| | |
| | | return taskScheduler.getRecurringTask(taskEntryDN); |
| | | } |
| | | |
| | | |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public File getDirectory() |
| | | { |
| | |
| | | }; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public ListIterator<Path> getFilesToBackup() throws DirectoryException |
| | | { |
| | | return BackupManager.getFiles(getDirectory(), getFilesToBackupFilter(), getBackendID()).listIterator(); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public boolean isDirectRestore() |
| | | { |
| | | return true; |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public Path beforeRestore() throws DirectoryException |
| | | { |
| | |
| | | return BackupManager.saveCurrentFilesToDirectory(this, getBackendID()); |
| | | } |
| | | |
| | | /** {@inheritDoc} */ |
| | | @Override |
| | | public void afterRestore(Path restoreDirectory, Path saveDirectory) throws DirectoryException |
| | | { |
| | | // restore was successful, delete the save directory |
| | | StaticUtils.recursiveDelete(saveDirectory.toFile()); |
| | | } |
| | | |
| | | } |
| | | |