| | |
| | | package org.opends.server.replication.plugin; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.Hashtable; |
| | | import java.util.Iterator; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | |
| | | import org.opends.messages.Message; |
| | | import org.opends.server.admin.server.ConfigurationChangeListener; |
| | | import org.opends.server.admin.server.ServerManagementContext; |
| | | import org.opends.server.admin.std.server.FractionalLDIFImportPluginCfg; |
| | | import org.opends.server.admin.std.server.PluginCfg; |
| | | import org.opends.server.admin.std.server.ReplicationDomainCfg; |
| | | import org.opends.server.admin.std.server.ReplicationSynchronizationProviderCfg; |
| | | import org.opends.server.admin.std.server.RootCfg; |
| | | import org.opends.server.api.plugin.*; |
| | | import org.opends.server.config.ConfigException; |
| | | import org.opends.server.core.DirectoryServer; |
| | | import org.opends.server.replication.plugin.LDAPReplicationDomain. |
| | | AttributeValueStringIterator; |
| | | import org.opends.server.replication.plugin.LDAPReplicationDomain. |
| | | FractionalConfig; |
| | | import org.opends.server.types.*; |
| | | |
| | | import static org.opends.messages.ReplicationMessages.*; |
| | | |
| | | /** |
| | | * This class implements a Directory Server plugin that is used in fractional |
| | | * replication when an online full update occurs. |
| | | * replication to initialize a just configured fractional domain (when an online |
| | | * full update occurs or offline/online ldif import). |
| | | * The following tasks are done: |
| | | * - check that the fractional configuration (if any) stored in the root entry |
| | | * of the domain is compliant with the fractional configuration of the domain |
| | | * (if not make online update stop) |
| | | * - check that the fractional configuration (if any) stored in the (incoming) |
| | | * root entry of the domain is compliant with the fractional configuration of |
| | | * the domain (if not make online update stop) |
| | | * - perform filtering according to fractional configuration of the domain |
| | | * - flush the fractional configuration of the domain in the root entry |
| | | * (if no one already present) |
| | |
| | | extends DirectoryServerPlugin<FractionalLDIFImportPluginCfg> |
| | | implements ConfigurationChangeListener<FractionalLDIFImportPluginCfg> |
| | | { |
| | | // Holds the fractional configuration and if available the replication domain |
| | | // matching this import session (they form the importfractional context). |
| | | // Domain is available if the server is online (import-ldif, online full |
| | | // update..) otherwise, this is an import-ldif with server off. The key is the |
| | | // ImportConfig object of the session which acts as a cookie for the whole |
| | | // session. This allows to potentially run man imports at the same time. |
| | | private final Hashtable<LDIFImportConfig, ImportFractionalContext> |
| | | importSessionContexts = new Hashtable<LDIFImportConfig, |
| | | ImportFractionalContext>(); |
| | | |
| | | /** |
| | | * Holds an import session fractional context. |
| | | */ |
| | | private static class ImportFractionalContext |
| | | { |
| | | // Fractional configuration of the local domain (may be null if import on a |
| | | // not replicated domain) |
| | | private FractionalConfig fractionalConfig = null; |
| | | // The local domain object (may stay null if server is offline) |
| | | private LDAPReplicationDomain domain = null; |
| | | |
| | | /** |
| | | * Constructor. |
| | | * @param fractionalConfig |
| | | * @param domain |
| | | */ |
| | | public ImportFractionalContext(FractionalConfig fractionalConfig, |
| | | LDAPReplicationDomain domain) |
| | | { |
| | | this.fractionalConfig = fractionalConfig; |
| | | this.domain = domain; |
| | | } |
| | | |
| | | /** |
| | | * Getter for the fractional configuration. |
| | | * @return the fractionalConfig |
| | | */ |
| | | public FractionalConfig getFractionalConfig() |
| | | { |
| | | return fractionalConfig; |
| | | } |
| | | |
| | | /** |
| | | * Getter for the domain.. |
| | | * @return the domain |
| | | */ |
| | | public LDAPReplicationDomain getDomain() |
| | | { |
| | | return domain; |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * Creates a new instance of this Directory Server plugin. Every plugin must |
| | |
| | | switch (t) |
| | | { |
| | | case LDIF_IMPORT: |
| | | case LDIF_IMPORT_END: |
| | | // This is acceptable. |
| | | break; |
| | | |
| | |
| | | @Override() |
| | | public final void finalizePlugin() |
| | | { |
| | | // Nothing to do |
| | | } |
| | | |
| | | /** |
| | | * Attempts to retrieve the fractional configuration of the domain being |
| | | * imported. |
| | | * @param entry An imported entry of the imported domain |
| | | * @return The parsed fractional configuration for the domain matching the |
| | | * passed entry. Null if no configuration is found for the domain |
| | | * (not a replicated domain). |
| | | */ |
| | | private static FractionalConfig getStaticReplicationDomainFractionalConfig( |
| | | Entry entry) throws Exception { |
| | | |
| | | // Retrieve the configuration |
| | | ServerManagementContext context = ServerManagementContext.getInstance(); |
| | | RootCfg root = context.getRootConfiguration(); |
| | | |
| | | |
| | | ReplicationSynchronizationProviderCfg sync = |
| | | (ReplicationSynchronizationProviderCfg) |
| | | root.getSynchronizationProvider("Multimaster Synchronization"); |
| | | |
| | | String[] domainNames = sync.listReplicationDomains(); |
| | | if (domainNames == null) |
| | | { |
| | | // No domain in replication |
| | | return null; |
| | | } |
| | | |
| | | // Find the configuration for domain the entry is part of |
| | | ReplicationDomainCfg matchingReplicatedDomainCfg = null; |
| | | for (String domainName : domainNames) |
| | | { |
| | | // Get the domain configuration object |
| | | ReplicationDomainCfg replicationDomainCfg = |
| | | sync.getReplicationDomain(domainName); |
| | | // Is the entry a sub entry of the replicated domain main entry ? |
| | | DN replicatedDn = replicationDomainCfg.getBaseDN(); |
| | | DN entryDn = entry.getDN(); |
| | | if (entryDn.isDescendantOf(replicatedDn)) |
| | | { |
| | | // Found the matching replicated domain configuration object |
| | | matchingReplicatedDomainCfg = replicationDomainCfg; |
| | | break; |
| | | } |
| | | } |
| | | |
| | | if (matchingReplicatedDomainCfg == null) |
| | | { |
| | | // No matching replicated domain found |
| | | return null; |
| | | } |
| | | |
| | | // Extract the fractional configuration from the domain configuration object |
| | | // and return it. |
| | | return FractionalConfig.toFractionalConfig(matchingReplicatedDomainCfg); |
| | | } |
| | | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | | public final void doLDIFImportEnd( |
| | | LDIFImportConfig importConfig) |
| | | { |
| | | // Remove the cookie of this import session |
| | | synchronized(importSessionContexts) |
| | | { |
| | | importSessionContexts.remove(importConfig); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * See class comment for what we achieve here... |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override() |
| | |
| | | LDIFImportConfig importConfig, Entry entry) |
| | | { |
| | | /** |
| | | * See class comment for what we achieve here... |
| | | * try to get the import fractional context for this entry. If not found, |
| | | * create and initialize it. The mechanism here is done to take a lock only |
| | | * once for the whole import session (except the necessary lock of the |
| | | * doLDIFImportEnd method) |
| | | */ |
| | | ImportFractionalContext importFractionalContext = |
| | | importSessionContexts.get(importConfig); |
| | | |
| | | // Retrieve the replicated domain this entry belongs to |
| | | DN entryDn = entry.getDN(); |
| | | LDAPReplicationDomain domain = MultimasterReplication.findDomain(entryDn, |
| | | null); |
| | | if (domain == null) |
| | | FractionalConfig localFractionalConfig = null; |
| | | |
| | | // If no context, create it |
| | | if (importFractionalContext == null) |
| | | { |
| | | synchronized(importSessionContexts) |
| | | { |
| | | // Insure antoher thread was not creating the context at the same time |
| | | // (we would create it for the second time which is useless) |
| | | importFractionalContext = importSessionContexts.get(importConfig); |
| | | if (importFractionalContext == null) |
| | | { |
| | | /* |
| | | * Create context |
| | | */ |
| | | |
| | | /** |
| | | * Retrieve the replicated domain this entry belongs to. Try to |
| | | * retrieve replication domain instance first. If we are in an online |
| | | * server, we should get it (if we are treating an entry that belongs |
| | | * to a replicated domain), otherwise the domain is not replicated or |
| | | * we are in an offline server context (import-ldif command run with |
| | | * offline server) and we must retrieve the fractional configuration |
| | | * directly from the configuration management system. |
| | | */ |
| | | LDAPReplicationDomain domain = |
| | | MultimasterReplication.findDomain(entryDn, null); |
| | | |
| | | // Get the fractional configuration extracted from the local server |
| | | // configuration for the currently imported domain |
| | | if (domain == null) |
| | | { |
| | | // Server may be offline, attempt to find fractional configuration |
| | | // from config sub-system |
| | | try |
| | | { |
| | | localFractionalConfig = |
| | | getStaticReplicationDomainFractionalConfig(entry); |
| | | } catch (Exception ex) |
| | | { |
| | | Message message = ERR_FRACTIONAL_COULD_NOT_RETRIEVE_CONFIG.get( |
| | | entry.toString()); |
| | | return PluginResult.ImportLDIF.stopEntryProcessing(message); |
| | | } |
| | | } else |
| | | { |
| | | // Found a live domain, retrieve the fractional configuration from |
| | | // it. |
| | | localFractionalConfig = domain.getFractionalConfig(); |
| | | } |
| | | // Create context and store it |
| | | importFractionalContext = |
| | | new ImportFractionalContext(localFractionalConfig, domain); |
| | | importSessionContexts.put(importConfig, importFractionalContext); |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Extract the fractional configuration from the context |
| | | localFractionalConfig = importFractionalContext.getFractionalConfig(); |
| | | if (localFractionalConfig == null) |
| | | { |
| | | // Not part of a replicated domain : nothing to do |
| | | return PluginResult.ImportLDIF.continueEntryProcessing(); |
| | | } |
| | | |
| | | // Is the entry to treat the root entry of the domain ? If yes, analyze the |
| | | // fractional configuration in it and compare with local domain fractional |
| | | // configuration. Stop the import if some inconsistency is detcted |
| | | DN domainBaseDn = domain.getBaseDN(); |
| | | if (domainBaseDn.equals(entryDn)) |
| | | /** |
| | | * At this point, either the domain instance has been found and we use its |
| | | * fractional configuration, or the server is offline and we use the parsed |
| | | * fractional configuration. We differentiate both cases testing if domain |
| | | * is null. We are also for sure handling an entry of a replicated suffix. |
| | | */ |
| | | |
| | | // Is the entry to handle the root entry of the domain ? If yes, analyze the |
| | | // fractional configuration in it and compare with local fractional |
| | | // configuration. Stop the import if some inconsistency is detected |
| | | DN replicatedDomainBaseDn = localFractionalConfig.getBaseDn(); |
| | | if (replicatedDomainBaseDn.equals(entryDn)) |
| | | { |
| | | /* |
| | | * This is the root entry, try to read a fractional configuration from it |
| | |
| | | } |
| | | |
| | | // Compare backend and local fractional configuration |
| | | boolean sameConfig = domain.isFractionalConfigConsistent(exclIt, inclIt); |
| | | if (domain.isFractional()) |
| | | boolean sameConfig = LDAPReplicationDomain. |
| | | isFractionalConfigConsistent(localFractionalConfig, exclIt, inclIt); |
| | | if (localFractionalConfig.isFractional()) |
| | | { |
| | | // Local domain is fractional |
| | | if (sameConfig) |
| | |
| | | } |
| | | if (remoteDomainHasSomeConfig) |
| | | { |
| | | // Local domain is fractional, remote domain has some config which |
| | | // is different : stop import (error will be logged when import is |
| | | // stopped) |
| | | domain.setImportErrorMessageId( |
| | | LDAPReplicationDomain.IMPORT_ERROR_MESSAGE_BAD_REMOTE); |
| | | domain.setFollowImport(false); |
| | | return PluginResult.ImportLDIF.continueEntryProcessing(); |
| | | LDAPReplicationDomain domain = importFractionalContext.getDomain(); |
| | | if (domain != null) |
| | | { |
| | | // Local domain is fractional, remote domain has some config which |
| | | // is different : stop import (error will be logged when import is |
| | | // stopped) |
| | | domain.setImportErrorMessageId( |
| | | LDAPReplicationDomain.IMPORT_ERROR_MESSAGE_BAD_REMOTE); |
| | | domain.setFollowImport(false); |
| | | return PluginResult.ImportLDIF.stopEntryProcessing(null); |
| | | } else |
| | | { |
| | | Message message = NOTE_ERR_LDIF_IMPORT_FRACTIONAL_BAD_DATA_SET. |
| | | get(replicatedDomainBaseDn.toString()); |
| | | return PluginResult.ImportLDIF.stopEntryProcessing(message); |
| | | } |
| | | } else |
| | | { |
| | | // Local domain is fractional but remote domain has no config : |
| | | // flush local config into root entry and follow import with |
| | | // filtering |
| | | flushFractionalConfigIntoEntry(domain, entry); |
| | | flushFractionalConfigIntoEntry(localFractionalConfig, entry); |
| | | } |
| | | } |
| | | } else |
| | |
| | | return PluginResult.ImportLDIF.continueEntryProcessing(); |
| | | } else |
| | | { |
| | | // Local domain is not fractional but remote one is : stop import : |
| | | // local domain should be configured with the same config as remote |
| | | // one |
| | | domain.setImportErrorMessageId( |
| | | LDAPReplicationDomain.IMPORT_ERROR_MESSAGE_REMOTE_IS_FRACTIONAL); |
| | | domain.setFollowImport(false); |
| | | return PluginResult.ImportLDIF.continueEntryProcessing(); |
| | | LDAPReplicationDomain domain = importFractionalContext.getDomain(); |
| | | if (domain != null) |
| | | { |
| | | // Local domain is not fractional but remote one is : stop import : |
| | | // local domain should be configured with the same config as remote |
| | | // one |
| | | domain.setImportErrorMessageId( |
| | | LDAPReplicationDomain. |
| | | IMPORT_ERROR_MESSAGE_REMOTE_IS_FRACTIONAL); |
| | | domain.setFollowImport(false); |
| | | return PluginResult.ImportLDIF.stopEntryProcessing(null); |
| | | } else |
| | | { |
| | | Message message = |
| | | NOTE_ERR_LDIF_IMPORT_FRACTIONAL_DATA_SET_IS_FRACTIONAL.get( |
| | | replicatedDomainBaseDn.toString()); |
| | | return PluginResult.ImportLDIF.stopEntryProcessing(message); |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | // If we get here, local domain fractional configuration is enabled. |
| | | // Now filter for potential attributes to be removed. |
| | | domain.fractionalRemoveAttributesFromEntry(entry.getDN().getRDN(), |
| | | LDAPReplicationDomain.fractionalRemoveAttributesFromEntry( |
| | | localFractionalConfig, entry.getDN().getRDN(), |
| | | entry.getObjectClasses(), entry.getUserAttributes(), true); |
| | | |
| | | return PluginResult.ImportLDIF.continueEntryProcessing(); |
| | |
| | | * WARNING: assumption is that no fractional attributes at all is already |
| | | * present in the passed entry. Also assumption is that domain fractional |
| | | * configuration is on. |
| | | * @param domain Domain containing the fractional configuration to use |
| | | * @param localFractionalConfig The local domain fractional configuration |
| | | * @param entry The entry to modify |
| | | */ |
| | | private static void flushFractionalConfigIntoEntry( |
| | | LDAPReplicationDomain domain, Entry entry) |
| | | private static void flushFractionalConfigIntoEntry(FractionalConfig |
| | | localFractionalConfig, Entry entry) |
| | | { |
| | | if (domain.isFractional()) // Paranoia check |
| | | if (localFractionalConfig.isFractional()) // Paranoia check |
| | | { |
| | | // Get the fractional configuration of the domain |
| | | boolean fractionalExclusive = domain.isFractionalExclusive(); |
| | | boolean fractionalExclusive = |
| | | localFractionalConfig.isFractionalExclusive(); |
| | | Map<String, List<String>> fractionalSpecificClassesAttributes = |
| | | domain.getFractionalSpecificClassesAttributes(); |
| | | localFractionalConfig.getFractionalSpecificClassesAttributes(); |
| | | List<String> fractionalAllClassesAttributes = |
| | | domain.getFractionalAllClassesAttributes(); |
| | | localFractionalConfig.getFractionalAllClassesAttributes(); |
| | | |
| | | // Create attribute builder for the rigth fractional mode |
| | | String fractAttribute = null; |