| | |
| | | * |
| | | * |
| | | * Copyright 2006-2010 Sun Microsystems, Inc. |
| | | * Portions Copyright 2011 ForgeRock AS |
| | | * Portions Copyright 2011-2012 ForgeRock AS |
| | | */ |
| | | |
| | | package org.opends.server.replication.plugin; |
| | |
| | | import org.opends.server.tasks.PurgeConflictsHistoricalTask; |
| | | |
| | | /** |
| | | * This class implements the bulk part of the.of the Directory Server side |
| | | * This class implements the bulk part of the Directory Server side |
| | | * of the replication code. |
| | | * It contains the root method for publishing a change, |
| | | * processing a change received from the replicationServer service, |
| | | * handle conflict resolution, |
| | | * handle protocol messages from the replicationServer. |
| | | */ |
| | | public class LDAPReplicationDomain extends ReplicationDomain |
| | | public final class LDAPReplicationDomain extends ReplicationDomain |
| | | implements ConfigurationChangeListener<ReplicationDomainCfg>, |
| | | AlertGenerator |
| | | { |
| | |
| | | this.endChangeNumber = endChangeNumber; |
| | | } |
| | | |
| | | @Override |
| | | public void handleInternalSearchEntry( |
| | | InternalSearchOperation searchOperation, SearchResultEntry searchEntry) |
| | | throws DirectoryException |
| | |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void handleInternalSearchReference( |
| | | InternalSearchOperation searchOperation, |
| | | SearchResultReference searchReference) throws DirectoryException |
| | |
| | | private final AtomicInteger numResolvedModifyConflicts = new AtomicInteger(); |
| | | private final AtomicInteger numUnresolvedNamingConflicts = |
| | | new AtomicInteger(); |
| | | private final int debugCount = 0; |
| | | private final PersistentServerState state; |
| | | private int numReplayedPostOpCalled = 0; |
| | | |
| | |
| | | configuration.getServerId(), |
| | | configuration.getInitializationWindowSize()); |
| | | |
| | | /** |
| | | * The time in milliseconds between heartbeats from the replication |
| | | * server. Zero means heartbeats are off. |
| | | */ |
| | | long heartbeatInterval = 0; |
| | | |
| | | // Read the configuration parameters. |
| | | Set<String> replicationServers = configuration.getReplicationServer(); |
| | | |
| | | this.serverId = configuration.getServerId(); |
| | | this.baseDn = configuration.getBaseDN(); |
| | | int window = configuration.getWindowSize(); |
| | | heartbeatInterval = configuration.getHeartbeatInterval(); |
| | | /** |
| | | * The time in milliseconds between heartbeats from the replication |
| | | * server. Zero means heartbeats are off. |
| | | */ |
| | | long heartbeatInterval = configuration.getHeartbeatInterval(); |
| | | |
| | | this.isolationPolicy = configuration.getIsolationPolicy(); |
| | | this.configDn = configuration.dn(); |
| | | this.logChangeNumber = configuration.isLogChangenumber(); |
| | |
| | | boolean allowReconnection) |
| | | { |
| | | // Read the configuration entry |
| | | FractionalConfig newFractionalConfig = null; |
| | | FractionalConfig newFractionalConfig; |
| | | try |
| | | { |
| | | newFractionalConfig = FractionalConfig.toFractionalConfig( |
| | |
| | | */ |
| | | |
| | | // Compute current configuration |
| | | boolean needReconnection = false; |
| | | boolean needReconnection; |
| | | try |
| | | { |
| | | needReconnection = !FractionalConfig. |
| | |
| | | |
| | | // Disable service if configuration changed |
| | | if (needReconnection && allowReconnection) |
| | | { |
| | | disableService(); |
| | | |
| | | } |
| | | // Set new configuration |
| | | int newFractionalMode = newFractionalConfig.fractionalConfigToInt(); |
| | | fractionalConfig.setFractional(newFractionalMode != |
| | |
| | | * Search the domain root entry that is used to save the generation id |
| | | */ |
| | | |
| | | InternalSearchOperation search = null; |
| | | LinkedHashSet<String> attributes = new LinkedHashSet<String>(1); |
| | | attributes.add(REPLICATION_GENERATION_ID); |
| | | attributes.add(REPLICATION_FRACTIONAL_EXCLUDE); |
| | | attributes.add(REPLICATION_FRACTIONAL_INCLUDE); |
| | | search = conn.processSearch(asn1BaseDn, |
| | | InternalSearchOperation search = conn.processSearch(asn1BaseDn, |
| | | SearchScope.BASE_OBJECT, |
| | | DereferencePolicy.DEREF_ALWAYS, 0, 0, false, |
| | | filter, attributes); |
| | |
| | | new HashMap<String, List<String>>(); |
| | | List<String> storedFractionalAllClassesAttributes = new ArrayList<String>(); |
| | | |
| | | int storedFractionalMode = FractionalConfig.NOT_FRACTIONAL; |
| | | int storedFractionalMode; |
| | | try |
| | | { |
| | | storedFractionalMode = FractionalConfig.parseFractionalConfig(exclIt, |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean hasNext() |
| | | { |
| | | return attrValIt.hasNext(); |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public String next() |
| | | { |
| | | return attrValIt.next().getValue().toString(); |
| | |
| | | * {@inheritDoc} |
| | | */ |
| | | // Should not be needed anyway |
| | | @Override |
| | | public void remove() |
| | | { |
| | | attrValIt.remove(); |
| | |
| | | concernedEntry.getObjectClasses().keySet()); |
| | | |
| | | boolean fractionalExclusive = fractionalConfig.isFractionalExclusive(); |
| | | if ( fractionalExclusive && (fractionalConcernedAttributes.size() == 0) ) |
| | | if ( fractionalExclusive && (fractionalConcernedAttributes.isEmpty()) ) |
| | | // No attributes to filter |
| | | return false; |
| | | |
| | |
| | | List<String> fractionalConcernedAttributes = |
| | | createFractionalConcernedAttrList(fractionalConfig, classes.keySet()); |
| | | boolean fractionalExclusive = fractionalConfig.isFractionalExclusive(); |
| | | if ( fractionalExclusive && (fractionalConcernedAttributes.size() == 0) ) |
| | | if ( fractionalExclusive && (fractionalConcernedAttributes.isEmpty()) ) |
| | | return false; // No attributes to filter |
| | | |
| | | // Prepare list of object classes of the added entry |
| | |
| | | } |
| | | // Now overwrite the attribute values for the attribute types present in the |
| | | // RDN, if there are some filtered attributes in the RDN |
| | | int index = 0; |
| | | for (index = 0 ; index < rdnAttrTypes.size() ; index++) |
| | | for (int index = 0 ; index < rdnAttrTypes.size() ; index++) |
| | | { |
| | | attributesMap.put(rdnAttrTypes.get(index), newRdnAttrLists.get(index)); |
| | | } |
| | |
| | | createFractionalConcernedAttrList(fractionalConfig, |
| | | modifiedEntry.getObjectClasses().keySet()); |
| | | boolean fractionalExclusive = fractionalConfig.isFractionalExclusive(); |
| | | if ( fractionalExclusive && (fractionalConcernedAttributes.size() == 0) ) |
| | | if ( fractionalExclusive && (fractionalConcernedAttributes.isEmpty()) ) |
| | | // No attributes to filter |
| | | return FRACTIONAL_HAS_NO_FRACTIONAL_FILTERED_ATTRIBUTES; |
| | | |
| | | // Prepare list of object classes of the modified entry |
| | | DN entryToModifyDn = modifyOperation.getEntryDN(); |
| | | Entry entryToModify = null; |
| | | Entry entryToModify; |
| | | try |
| | | { |
| | | entryToModify = DirectoryServer.getEntry(entryToModifyDn); |
| | |
| | | // Found a modification to remove, remove it from the list. |
| | | modsIt.remove(); |
| | | result = FRACTIONAL_HAS_FRACTIONAL_FILTERED_ATTRIBUTES; |
| | | if (mods.size() == 0) |
| | | if (mods.isEmpty()) |
| | | { |
| | | // This operation must become a no-op as no more modification in |
| | | // it |
| | |
| | | private ModifyDNOperationBasis renameEntry( |
| | | DN targetDN, RDN newRDN, DN parentDN, boolean markConflict) |
| | | { |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | |
| | | ModifyDNOperationBasis newOp = |
| | | new ModifyDNOperationBasis( |
| | | conn, InternalClientConnection.nextOperationID(), |
| | | InternalClientConnection.nextMessageID(), new ArrayList<Control>(0), |
| | | targetDN, newRDN, false, |
| | | parentDN); |
| | | newOp.setInternalOperation(true); |
| | | newOp.setSynchronizationOperation(true); |
| | | newOp.setDontSynchronize(true); |
| | | new ModifyDNOperationBasis( |
| | | conn, InternalClientConnection.nextOperationID(), |
| | | InternalClientConnection.nextMessageID(), new ArrayList<Control>(0), |
| | | targetDN, newRDN, false, |
| | | parentDN); |
| | | newOp.setInternalOperation(true); |
| | | newOp.setSynchronizationOperation(true); |
| | | newOp.setDontSynchronize(true); |
| | | |
| | | if (markConflict) |
| | | { |
| | | AttributeType attrType = |
| | | DirectoryServer.getAttributeType(DS_SYNC_CONFLICT, true); |
| | | Attribute attr = Attributes.create(attrType, AttributeValues.create( |
| | | attrType, targetDN.toNormalizedString())); |
| | | Modification mod = new Modification(ModificationType.REPLACE, attr); |
| | | newOp.addModification(mod); |
| | | } |
| | | else |
| | | { |
| | | AttributeType attrType = |
| | | DirectoryServer.getAttributeType(DS_SYNC_CONFLICT, true); |
| | | Attribute attr = Attributes.empty(attrType); |
| | | Modification mod = new Modification(ModificationType.DELETE, attr); |
| | | newOp.addModification(mod); |
| | | } |
| | | if (markConflict) |
| | | { |
| | | AttributeType attrType = |
| | | DirectoryServer.getAttributeType(DS_SYNC_CONFLICT, true); |
| | | Attribute attr = Attributes.create(attrType, AttributeValues.create( |
| | | attrType, targetDN.toNormalizedString())); |
| | | Modification mod = new Modification(ModificationType.REPLACE, attr); |
| | | newOp.addModification(mod); |
| | | } |
| | | else |
| | | { |
| | | AttributeType attrType = |
| | | DirectoryServer.getAttributeType(DS_SYNC_CONFLICT, true); |
| | | Attribute attr = Attributes.empty(attrType); |
| | | Modification mod = new Modification(ModificationType.DELETE, attr); |
| | | newOp.addModification(mod); |
| | | } |
| | | |
| | | newOp.run(); |
| | | newOp.run(); |
| | | return newOp; |
| | | } |
| | | |
| | |
| | | return numReplayedPostOpCalled; |
| | | } |
| | | |
| | | /** |
| | | * Get the debugCount. |
| | | * |
| | | * @return Returns the debugCount. |
| | | */ |
| | | public int getDebugCount() |
| | | { |
| | | return debugCount; |
| | | } |
| | | |
| | | /** |
| | | * Delete this ReplicationDomain. |
| | |
| | | else if (result == ResultCode.NOT_ALLOWED_ON_RDN) |
| | | { |
| | | DN currentDN = findEntryDN(entryUid); |
| | | RDN currentRDN = null; |
| | | RDN currentRDN; |
| | | if (currentDN != null) |
| | | { |
| | | currentRDN = currentDN.getRDN(); |
| | |
| | | |
| | | // Construct the new DN to use for the entry. |
| | | DN entryDN = op.getEntryDN(); |
| | | DN newSuperior = null; |
| | | DN newSuperior; |
| | | RDN newRDN = op.getNewRDN(); |
| | | |
| | | if (newSuperiorID != null) |
| | |
| | | boolean conflict = false; |
| | | |
| | | // Find an rename child entries. |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | |
| | | try |
| | | { |
| | | LinkedHashSet<String> attrs = new LinkedHashSet<String>(1); |
| | |
| | | private void markConflictEntry(Operation op, DN currentDN, DN conflictDN) |
| | | { |
| | | // create new internal modify operation and run it. |
| | | InternalClientConnection conn = |
| | | InternalClientConnection.getRootConnection(); |
| | | |
| | | AttributeType attrType = DirectoryServer.getAttributeType(DS_SYNC_CONFLICT, |
| | | true); |
| | | Attribute attr = Attributes.create(attrType, AttributeValues.create( |
| | |
| | | protected void loadDataState() |
| | | throws DirectoryException |
| | | { |
| | | Long compatGenId = null; |
| | | |
| | | state.clearInMemory(); |
| | | state.loadState(); |
| | | |
| | | // Check to see if a Ruv needs to be translated |
| | | compatGenId = state.checkRUVCompat(); |
| | | Long compatGenId = state.checkRUVCompat(); |
| | | |
| | | generator.adjust(state.getMaxChangeNumber(serverId)); |
| | | // Retrieves the generation ID associated with the data imported |
| | |
| | | * @return generationId The retrieved value of generationId |
| | | * @throws DirectoryException When an error occurs. |
| | | */ |
| | | public long loadGenerationId() |
| | | private long loadGenerationId() |
| | | throws DirectoryException |
| | | { |
| | | long generationId=-1; |
| | | long aGenerationId=-1; |
| | | |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo( |
| | |
| | | * Search the database entry that is used to periodically |
| | | * save the generation id |
| | | */ |
| | | SearchResultEntry resultEntry = null; |
| | | InternalSearchOperation search = null; |
| | | LinkedHashSet<String> attributes = new LinkedHashSet<String>(1); |
| | | attributes.add(REPLICATION_GENERATION_ID); |
| | | search = conn.processSearch(asn1BaseDn, |
| | | InternalSearchOperation search = conn.processSearch(asn1BaseDn, |
| | | SearchScope.BASE_OBJECT, |
| | | DereferencePolicy.DEREF_ALWAYS, 0, 0, false, |
| | | filter,attributes); |
| | |
| | | else |
| | | { |
| | | LinkedList<SearchResultEntry> result = search.getSearchEntries(); |
| | | resultEntry = result.getFirst(); |
| | | SearchResultEntry resultEntry = result.getFirst(); |
| | | if (resultEntry != null) |
| | | { |
| | | AttributeType synchronizationGenIDType = |
| | |
| | | found=true; |
| | | try |
| | | { |
| | | generationId = Long.decode(attr.iterator().next().toString()); |
| | | aGenerationId = Long.decode(attr.iterator().next().toString()); |
| | | } |
| | | catch(Exception e) |
| | | { |
| | |
| | | |
| | | if (!found) |
| | | { |
| | | generationId = computeGenerationId(); |
| | | saveGenerationId(generationId); |
| | | aGenerationId = computeGenerationId(); |
| | | saveGenerationId(aGenerationId); |
| | | |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo("Generation ID created for domain base DN=" + |
| | | baseDn.toString() + |
| | | " generationId=" + generationId); |
| | | " generationId=" + aGenerationId); |
| | | } |
| | | else |
| | | { |
| | |
| | | if (debugEnabled()) |
| | | TRACER.debugInfo( |
| | | "Generation ID successfully read from domain base DN=" + baseDn + |
| | | " generationId=" + generationId); |
| | | " generationId=" + aGenerationId); |
| | | } |
| | | return generationId; |
| | | return aGenerationId; |
| | | } |
| | | |
| | | /** |
| | |
| | | ResultCode.OTHER, message, null); |
| | | } |
| | | |
| | | OutputStream os = null; |
| | | OutputStream os; |
| | | ReplLDIFOutputStream ros = null; |
| | | |
| | | if (checksumOutput) |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public ConfigChangeResult applyConfigurationChange( |
| | | ReplicationDomainCfg configuration) |
| | | { |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public boolean isConfigurationChangeAcceptable( |
| | | ReplicationDomainCfg configuration, List<Message> unacceptableReasons) |
| | | { |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public LinkedHashMap<String, String> getAlerts() |
| | | { |
| | | LinkedHashMap<String,String> alerts = new LinkedHashMap<String,String>(); |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public String getClassName() |
| | | { |
| | | return CLASS_NAME; |
| | |
| | | /** |
| | | * {@inheritDoc} |
| | | */ |
| | | @Override |
| | | public DN getComponentEntryDN() |
| | | { |
| | | return configDn; |
| | |
| | | } |
| | | } |
| | | |
| | | ChangeNumber lastRetrievedChange = null; |
| | | ChangeNumber lastRetrievedChange; |
| | | long missingChangesDelta; |
| | | InternalSearchOperation op; |
| | | ChangeNumber currentStartChangeNumber = startingChangeNumber; |
| | |
| | | */ |
| | | int fractionalConfigToInt() |
| | | { |
| | | int fractionalMode = -1; |
| | | int fractionalMode; |
| | | if (fractional) |
| | | { |
| | | if (fractionalExclusive) |