/* * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * 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 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. 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 2006-2007 Sun Microsystems, Inc. */ package org.opends.server.replication.server; import static org.opends.messages.BackendMessages.*; import static org.opends.server.loggers.debug.DebugLogger.debugEnabled; import static org.opends.server.loggers.debug.DebugLogger.getTracer; import static org.opends.server.util.StaticUtils.getExceptionMessage; import java.util.HashMap; import java.util.HashSet; import org.opends.messages.Message; import org.opends.server.admin.Configuration; import org.opends.server.admin.std.server.BackendCfg; import org.opends.server.admin.std.server.JEBackendCfg; import org.opends.server.api.Backend; import org.opends.server.backends.jeb.BackupManager; import org.opends.server.config.ConfigException; import org.opends.server.core.AddOperation; import org.opends.server.core.DeleteOperation; import org.opends.server.core.DirectoryServer; import org.opends.server.core.ModifyDNOperation; import org.opends.server.core.ModifyOperation; import org.opends.server.core.SearchOperation; import org.opends.server.loggers.debug.DebugTracer; import org.opends.server.types.BackupConfig; import org.opends.server.types.BackupDirectory; import org.opends.server.types.ConditionResult; import org.opends.server.types.DN; import org.opends.server.types.DebugLogLevel; import org.opends.server.types.DirectoryException; import org.opends.server.types.Entry; import org.opends.server.types.InitializationException; import org.opends.server.types.LDIFExportConfig; import org.opends.server.types.LDIFImportConfig; import org.opends.server.types.LDIFImportResult; import org.opends.server.types.RestoreConfig; import org.opends.server.types.ResultCode; import org.opends.server.util.Validator; /** * This class defines a backend that stores its information in an * associated replication server object. * This is primarily intended to take advantage of the backup/restore/ * import/export of the backend API, and to provide an LDAP access * to the replication server database. *

* Entries stored in this backend are held in the DB associated with * the replication server. *

* Currently are only implemented the create and restore backup features. * */ public class ReplicationBackend extends Backend { /** * The tracer object for the debug logger. */ private static final DebugTracer TRACER = getTracer(); // The base DNs for this backend. private DN[] baseDNs; // The mapping between parent DNs and their immediate children. private HashMap> childDNs; // The base DNs for this backend, in a hash set. private HashSet baseDNSet; // The set of supported controls for this backend. private HashSet supportedControls; // The set of supported features for this backend. private HashSet supportedFeatures; // The directory associated with this backend. private BackupDirectory backendDirectory; ReplicationServer server; /** * The configuration of this backend. */ private JEBackendCfg cfg; /** * Creates a new backend with the provided information. All backend * implementations must implement a default constructor that use * super() to invoke this constructor. */ public ReplicationBackend() { super(); // Perform all initialization in initializeBackend. } /** * Set the base DNs for this backend. This is used by the unit tests * to set the base DNs without having to provide a configuration * object when initializing the backend. * @param baseDNs The set of base DNs to be served by this memory backend. */ public void setBaseDNs(DN[] baseDNs) { this.baseDNs = baseDNs; } /** * {@inheritDoc} */ public void configureBackend(Configuration config) throws ConfigException { if (config != null) { Validator.ensureTrue(config instanceof BackendCfg); cfg = (JEBackendCfg)config; DN[] baseDNs = new DN[cfg.getBackendBaseDN().size()]; cfg.getBackendBaseDN().toArray(baseDNs); setBaseDNs(baseDNs); backendDirectory = new BackupDirectory( cfg.getBackendDirectory(), null); } } /** * {@inheritDoc} */ public synchronized void initializeBackend() throws ConfigException, InitializationException { if ((baseDNs == null) || (baseDNs.length != 1)) { Message message = ERR_MEMORYBACKEND_REQUIRE_EXACTLY_ONE_BASE.get(); throw new ConfigException(message); } baseDNSet = new HashSet(); for (DN dn : baseDNs) { baseDNSet.add(dn); } childDNs = new HashMap>(); supportedControls = new HashSet(); supportedFeatures = new HashSet(); for (DN dn : baseDNs) { try { DirectoryServer.registerBaseDN(dn, this, false, false); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_BACKEND_CANNOT_REGISTER_BASEDN.get( dn.toString(), getExceptionMessage(e)); throw new InitializationException(message, e); } } } /** * Removes any data that may have been stored in this backend. */ public synchronized void clearMemoryBackend() { childDNs.clear(); } /** * {@inheritDoc} */ public synchronized void finalizeBackend() { for (DN dn : baseDNs) { try { DirectoryServer.deregisterBaseDN(dn, false); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } } } } /** * {@inheritDoc} */ public DN[] getBaseDNs() { return baseDNs; } /** * {@inheritDoc} */ public synchronized long getEntryCount() { return -1; } /** * {@inheritDoc} */ public boolean isLocal() { return true; } /** * {@inheritDoc} */ public synchronized Entry getEntry(DN entryDN) { return null; } /** * {@inheritDoc} */ public synchronized boolean entryExists(DN entryDN) { return false; } /** * {@inheritDoc} */ public synchronized void addEntry(Entry entry, AddOperation addOperation) throws DirectoryException { Message message = ERR_BACKUP_ADD_NOT_SUPPORTED.get(); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } /** * {@inheritDoc} */ public synchronized void deleteEntry(DN entryDN, DeleteOperation deleteOperation) throws DirectoryException { Message message = ERR_BACKUP_DELETE_NOT_SUPPORTED.get(); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } /** * {@inheritDoc} */ public synchronized void replaceEntry(Entry entry, ModifyOperation modifyOperation) throws DirectoryException { Message message = ERR_BACKUP_MODIFY_NOT_SUPPORTED.get(); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } /** * {@inheritDoc} */ public synchronized void renameEntry(DN currentDN, Entry entry, ModifyDNOperation modifyDNOperation) throws DirectoryException { Message message = ERR_BACKUP_MODIFY_DN_NOT_SUPPORTED.get(); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } /** * {@inheritDoc} */ public synchronized void search(SearchOperation searchOperation) throws DirectoryException { DN matchedDN = baseDNs[0]; DN baseDN = searchOperation.getBaseDN(); // FIXME Remove this error message or replace when implementing // the search. Message message = ERR_MEMORYBACKEND_ENTRY_DOESNT_EXIST.get(String.valueOf(baseDN)); throw new DirectoryException( ResultCode.NO_SUCH_OBJECT, message, matchedDN, null); } /** * {@inheritDoc} */ public HashSet getSupportedControls() { return supportedControls; } /** * {@inheritDoc} */ public HashSet getSupportedFeatures() { return supportedFeatures; } /** * {@inheritDoc} */ public boolean supportsLDIFExport() { return false; } /** * {@inheritDoc} */ public synchronized void exportLDIF(LDIFExportConfig exportConfig) throws DirectoryException { // TODO } /** * {@inheritDoc} */ public boolean supportsLDIFImport() { return false; } /** * {@inheritDoc} */ public synchronized LDIFImportResult importLDIF(LDIFImportConfig importConfig) throws DirectoryException { return new LDIFImportResult(0, 0, 0); } /** * {@inheritDoc} */ public boolean supportsBackup() { // This backend does not provide a backup/restore mechanism. return true; } /** * {@inheritDoc} */ public boolean supportsBackup(BackupConfig backupConfig, StringBuilder unsupportedReason) { return true; } /** * {@inheritDoc} */ public void createBackup(BackupConfig backupConfig) throws DirectoryException { BackupManager backupManager = new BackupManager(getBackendID()); backupManager.createBackup(cfg, backupConfig); } /** * {@inheritDoc} */ public void removeBackup(BackupDirectory backupDirectory, String backupID) throws DirectoryException { BackupManager backupManager = new BackupManager(getBackendID()); backupManager.removeBackup(this.backendDirectory, backupID); } /** * {@inheritDoc} */ public boolean supportsRestore() { return true; } /** * {@inheritDoc} */ public void restoreBackup(RestoreConfig restoreConfig) throws DirectoryException { BackupManager backupManager = new BackupManager(getBackendID()); backupManager.restoreBackup(cfg, restoreConfig); } /** * Retrieves the number of subordinates for the requested entry. * * @param entryDN The distinguished name of the entry. * * @return The number of subordinate entries for the requested entry * or -1 if it can not be determined. * * @throws DirectoryException If a problem occurs while trying to * retrieve the entry. */ public long numSubordinates(DN entryDN) throws DirectoryException { Message message = WARN_ROOTDSE_GET_ENTRY_NONROOT. get(entryDN.toNormalizedString()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } /** * Indicates whether the requested entry has any subordinates. * * @param entryDN The distinguished name of the entry. * * @return {@code ConditionResult.TRUE} if the entry has one or more * subordinates or {@code ConditionResult.FALSE} otherwise * or {@code ConditionResult.UNDEFINED} if it can not be * determined. * * @throws DirectoryException If a problem occurs while trying to * retrieve the entry. */ public ConditionResult hasSubordinates(DN entryDN) throws DirectoryException { Message message = WARN_ROOTDSE_GET_ENTRY_NONROOT. get(entryDN.toNormalizedString()); throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message); } /** * Set the replication server associated with this backend. * @param server The replication server. */ public void setServer(ReplicationServer server) { this.server = server; } }