/* * 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 Sun Microsystems, Inc. */ package org.opends.server.synchronization.plugin; import java.util.HashMap; import java.util.Map; import org.opends.server.api.ConfigAddListener; import org.opends.server.api.ConfigChangeListener; import org.opends.server.api.ConfigDeleteListener; import org.opends.server.api.SynchronizationProvider; import org.opends.server.config.ConfigEntry; import org.opends.server.config.ConfigException; import org.opends.server.core.AddOperation; import org.opends.server.synchronization.changelog.Changelog; import org.opends.server.synchronization.common.LogMessages; import org.opends.server.synchronization.common.ServerState; import org.opends.server.types.DN; import org.opends.server.core.DeleteOperation; import org.opends.server.types.DirectoryException; import org.opends.server.core.DirectoryServer; import org.opends.server.types.Entry; import org.opends.server.core.ModifyDNOperation; import org.opends.server.core.ModifyOperation; import org.opends.server.core.Operation; import org.opends.server.types.ConfigChangeResult; import org.opends.server.types.ResultCode; import org.opends.server.types.SynchronizationProviderResult; import static org.opends.server.synchronization.common.LogMessages.*; /** * This class is used to load the Synchronization code inside the JVM * and to trigger initialization of the synchronization. * * It also extends the SynchronizationProvider class in order to have some * synchronization code running during the operation process * as pre-op, conflictRsolution, and post-op. */ public class MultimasterSynchronization extends SynchronizationProvider implements ConfigAddListener, ConfigDeleteListener, ConfigChangeListener { static String CHANGELOG_DN = "cn=Changelog Server," + "cn=Multimaster Synchronization, cn=Synchronization Providers, cn=config"; static String SYNCHRONIZATION_CLASS = "ds-cfg-synchronization-provider-config"; private DN changelogConfigEntryDn = null; private Changelog changelog = null; private static Map domains = new HashMap() ; /** * Get the ServerState associated to the SynchronizationDomain * with a given DN. * * @param baseDn The DN of the Synchronization Domain for which the * ServerState must be returned. * @return the ServerState associated to the SynchronizationDomain * with the DN in parameter. */ public static ServerState getServerState(DN baseDn) { SynchronizationDomain domain = findDomain(baseDn); return domain.getServerState(); } /** * {@inheritDoc} */ public void initializeSynchronizationProvider(ConfigEntry configEntry) throws ConfigException { LogMessages.registerMessages(); configEntry.registerAddListener(this); configEntry.registerDeleteListener(this); /* * Read changelog server the changelog configuration entry */ try { changelogConfigEntryDn = DN.decode(CHANGELOG_DN); ConfigEntry config = DirectoryServer.getConfigEntry(changelogConfigEntryDn); /* * If there is no such entry, this process must not be a changelog server */ if (config != null) { changelog = new Changelog(config); } } catch (DirectoryException e) { /* never happens */ throw new ConfigException(MSGID_SYNC_INVALID_DN, "Invalid Changelog configuration DN"); } /* * Parse the list of entries below configEntry, * create one synchronization domain for each child */ for (ConfigEntry domainEntry : configEntry.getChildren().values()) { if (domainEntry.hasObjectClass(SYNCHRONIZATION_CLASS)) { createNewSynchronizationDomain(domainEntry); } } } /** * Indicates whether the configuration entry that will result from a proposed * modification is acceptable to this change listener. * * @param configEntry The configuration entry that will result from * the requested update. * @param unacceptableReason A buffer to which this method can append a * human-readable message explaining why the * proposed change is not acceptable. * * @return true if the proposed entry contains an acceptable * configuration, or false if it does not. */ public boolean configChangeIsAcceptable(ConfigEntry configEntry, StringBuilder unacceptableReason) { return false; // TODO :NYI } /** * Attempts to apply a new configuration to this Directory Server component * based on the provided changed entry. * * @param configEntry The configuration entry that containing the updated * configuration for this component. * * @return Information about the result of processing the configuration * change. */ public ConfigChangeResult applyConfigurationChange(ConfigEntry configEntry) { // TODO implement this method return new ConfigChangeResult(ResultCode.SUCCESS, false); } /** * {@inheritDoc} */ public boolean configAddIsAcceptable(ConfigEntry configEntry, StringBuilder unacceptableReason) { // Check if the added entry is the changelog config entry try { if (configEntry.getDN().equals(DN.decode(CHANGELOG_DN))) { return Changelog.checkConfigEntry(configEntry, unacceptableReason); } } catch (DirectoryException e) { /* never happens */ unacceptableReason.append("Invalid Changelog configuration DN"); return false; } // otherwise it must be a Synchronization domain, check for // presence of the Synchronization configuration object class if (configEntry.hasObjectClass(SYNCHRONIZATION_CLASS)) { return SynchronizationDomain.checkConfigEntry(configEntry, unacceptableReason); } return false; } /** * {@inheritDoc} */ public ConfigChangeResult applyConfigurationAdd(ConfigEntry configEntry) { // check if the entry is the changelog configuration entry if (configEntry.getDN().equals(changelogConfigEntryDn)) { try { changelog = new Changelog(configEntry); return new ConfigChangeResult(ResultCode.SUCCESS, false); } catch (ConfigException e) { // we should never get to this point because the configEntry has // already been validated in configAddisAcceptable return new ConfigChangeResult(ResultCode.SUCCESS, false); } } // otherwise it must be a synchronization domain, check for // presence of the Synchronization configuration object class if (configEntry.hasObjectClass(SYNCHRONIZATION_CLASS)) { try { createNewSynchronizationDomain(configEntry); return new ConfigChangeResult(ResultCode.SUCCESS, false); } catch (ConfigException e) { // we should never get to this point because the configEntry has // already been validated in configAddisAcceptable return new ConfigChangeResult(ResultCode.SUCCESS, false); } } // we should never get to this point because the configEntry has // already been validated in configAddisAcceptable return new ConfigChangeResult(ResultCode.SUCCESS, false); } /** * Creates a New Synchronization domain from its configEntry, do the * necessary initialization and starts it so that it is * fully operational when this method returns. * @param configEntry The entry whith the configuration of this domain. * @throws ConfigException When the configuration is not valid. */ private void createNewSynchronizationDomain(ConfigEntry configEntry) throws ConfigException { SynchronizationDomain domain; domain = new SynchronizationDomain(configEntry); domains.put(domain.getBaseDN(), domain); domain.start(); } /** * {@inheritDoc} */ public boolean configDeleteIsAcceptable(ConfigEntry configEntry, StringBuilder unacceptableReason) { // TODO Auto-generated method stub return true; } /** * {@inheritDoc} */ public ConfigChangeResult applyConfigurationDelete(ConfigEntry configEntry) { // TODO Auto-generated method stub return null; } /** * {@inheritDoc} */ public void doPostOperation(AddOperation addOperation) { DN dn = addOperation.getEntryDN(); genericPostOperation(addOperation, dn); } /** * {@inheritDoc} */ public void doPostOperation(DeleteOperation deleteOperation) { DN dn = deleteOperation.getEntryDN(); genericPostOperation(deleteOperation, dn); } /** * {@inheritDoc} */ public void doPostOperation(ModifyDNOperation modifyDNOperation) { DN dn = modifyDNOperation.getEntryDN(); genericPostOperation(modifyDNOperation, dn); } /** * {@inheritDoc} */ @Override public void doPostOperation(ModifyOperation modifyOperation) { DN dn = modifyOperation.getEntryDN(); genericPostOperation(modifyOperation, dn); } /** * {@inheritDoc} */ @Override public SynchronizationProviderResult handleConflictResolution( ModifyOperation modifyOperation) { SynchronizationDomain domain = findDomain(modifyOperation.getEntryDN()); if (domain == null) return new SynchronizationProviderResult(true); return domain.handleConflictResolution(modifyOperation); } /** * {@inheritDoc} */ @Override public SynchronizationProviderResult handleConflictResolution( AddOperation addOperation) throws DirectoryException { SynchronizationDomain domain = findDomain(addOperation.getEntryDN()); if (domain == null) return new SynchronizationProviderResult(true); return domain.handleConflictResolution(addOperation); } /** * {@inheritDoc} */ @Override public SynchronizationProviderResult handleConflictResolution( DeleteOperation deleteOperation) throws DirectoryException { SynchronizationDomain domain = findDomain(deleteOperation.getEntryDN()); if (domain == null) return new SynchronizationProviderResult(true); return domain.handleConflictResolution(deleteOperation); } /** * {@inheritDoc} */ @Override public SynchronizationProviderResult handleConflictResolution( ModifyDNOperation modifyDNOperation) throws DirectoryException { SynchronizationDomain domain = findDomain(modifyDNOperation.getEntryDN()); if (domain == null) return new SynchronizationProviderResult(true); return domain.handleConflictResolution(modifyDNOperation); } /** * {@inheritDoc} */ @Override public SynchronizationProviderResult doPreOperation(ModifyOperation modifyOperation) { SynchronizationDomain domain = findDomain(modifyOperation.getEntryDN()); if (domain == null) return new SynchronizationProviderResult(true); Historical historicalInformation = (Historical) modifyOperation.getAttachment(HISTORICAL); if (historicalInformation == null) { Entry entry = modifyOperation.getModifiedEntry(); historicalInformation = Historical.load(entry); modifyOperation.setAttachment(HISTORICAL, historicalInformation); } historicalInformation.generateState(modifyOperation); return new SynchronizationProviderResult(true); } /** * {@inheritDoc} */ @Override public SynchronizationProviderResult doPreOperation( DeleteOperation deleteOperation) throws DirectoryException { return new SynchronizationProviderResult(true); } /** * {@inheritDoc} */ @Override public SynchronizationProviderResult doPreOperation( ModifyDNOperation modifyDNOperation) throws DirectoryException { return new SynchronizationProviderResult(true); } /** * {@inheritDoc} */ @Override public SynchronizationProviderResult doPreOperation(AddOperation addOperation) { SynchronizationDomain domain = findDomain(addOperation.getEntryDN()); if (domain == null) return new SynchronizationProviderResult(true); if (!addOperation.isSynchronizationOperation()) domain.doPreOperation(addOperation); return new SynchronizationProviderResult(true); } /** * {@inheritDoc} */ @Override public void finalizeSynchronizationProvider() { // shutdown all the Synchronization domains for (SynchronizationDomain domain : domains.values()) { domain.shutdown(); } // shutdown the Changelog Service if necessary if (changelog != null) changelog.shutdown(); } /** * Finds the Synchronization domain for a given DN. * * @param dn The DN for which the domain must be returned. * @return The Synchronization domain for this DN. */ private static SynchronizationDomain findDomain(DN dn) { SynchronizationDomain domain = null; DN temp = dn; do { domain = domains.get(temp); temp = temp.getParentDNInSuffix(); if (temp == null) { break; } } while (domain == null); /* * Don't apply synchronization to the special entry where the ServerState * is stored. */ if ((domain!= null) && (domain.getServerStateDN().equals(dn))) return null; return domain; } /** * Generic code for all the postOperation entry point. * * @param operation The Operation for which the post-operation is called. * @param dn The Dn for which the post-operation is called. */ private void genericPostOperation(Operation operation, DN dn) { SynchronizationDomain domain = findDomain(dn); if (domain == null) return; domain.synchronize(operation); return; } }