5578N/A/*
5578N/A * CDDL HEADER START
5578N/A *
5578N/A * The contents of this file are subject to the terms of the
5578N/A * Common Development and Distribution License, Version 1.0 only
5578N/A * (the "License"). You may not use this file except in compliance
5578N/A * with the License.
5578N/A *
5578N/A * You can obtain a copy of the license at
5578N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE
5578N/A * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
5578N/A * See the License for the specific language governing permissions
5578N/A * and limitations under the License.
5578N/A *
5578N/A * When distributing Covered Code, include this CDDL HEADER in each
5578N/A * file and include the License file at
5578N/A * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
5578N/A * add the following below this CDDL HEADER, with the fields enclosed
5578N/A * by brackets "[]" replaced with your own identifying information:
5578N/A * Portions Copyright [yyyy] [name of copyright owner]
5578N/A *
5578N/A * CDDL HEADER END
5578N/A *
5578N/A *
5578N/A * Copyright 2011 profiq s.r.o.
5578N/A * Portions copyright 2011 ForgeRock AS.
5578N/A */
5578N/Apackage org.opends.server.plugins;
5578N/A
5578N/A
5578N/A
5578N/Aimport static org.opends.messages.PluginMessages.*;
5578N/Aimport static org.opends.server.loggers.debug.DebugLogger.debugEnabled;
5578N/Aimport static org.opends.server.util.StaticUtils.toLowerCase;
5578N/A
5578N/Aimport java.util.*;
5578N/Aimport java.util.concurrent.locks.ReentrantReadWriteLock;
5578N/Aimport java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
5578N/Aimport java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
5578N/A
5578N/Aimport org.opends.messages.Message;
5578N/Aimport org.opends.server.admin.server.ConfigurationChangeListener;
5578N/Aimport org.opends.server.admin.std.server.AttributeCleanupPluginCfg;
5578N/Aimport org.opends.server.admin.std.server.PluginCfg;
5578N/Aimport org.opends.server.api.plugin.DirectoryServerPlugin;
5578N/Aimport org.opends.server.api.plugin.PluginResult;
5578N/Aimport org.opends.server.api.plugin.PluginType;
5578N/Aimport org.opends.server.config.ConfigException;
5578N/Aimport org.opends.server.core.DirectoryServer;
5578N/Aimport org.opends.server.loggers.debug.DebugLogger;
5578N/Aimport org.opends.server.loggers.debug.DebugTracer;
5578N/Aimport org.opends.server.types.*;
5578N/Aimport org.opends.server.types.operation.PreParseAddOperation;
5578N/Aimport org.opends.server.types.operation.PreParseModifyOperation;
5578N/A
5578N/A
5578N/A
5578N/A/**
5578N/A * The attribute cleanup plugin implementation class. The plugin removes and/or
5578N/A * renames the configured parameters from the incoming ADD and MODIFY requests.
5578N/A */
5578N/Apublic class AttributeCleanupPlugin extends
5578N/A DirectoryServerPlugin<AttributeCleanupPluginCfg> implements
5578N/A ConfigurationChangeListener<AttributeCleanupPluginCfg>
5578N/A{
5578N/A
5578N/A /**
5578N/A * Plugin configuration.
5578N/A */
5578N/A private AttributeCleanupPluginCfg config;
5578N/A
5578N/A /**
5578N/A * Debug tracer.
5578N/A */
5578N/A private static final DebugTracer TRACER = DebugLogger.getTracer();
5578N/A
5578N/A /**
5578N/A * A table of attributes to be renamed.
5578N/A */
5578N/A private Map<String, String> attributesToRename;
5578N/A
5578N/A /**
5578N/A * The set of attributes to be removed.
5578N/A */
5578N/A private Set<String> attributesToRemove;
5578N/A
5578N/A /**
5578N/A * This lock prevents concurrent updates to the configuration while operations
5578N/A * are being processed.
5578N/A */
5578N/A private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
5578N/A private final ReadLock sharedLock = lock.readLock();
5578N/A private final WriteLock exclusiveLock = lock.writeLock();
5578N/A
5578N/A
5578N/A
5578N/A /**
5578N/A * Default constructor.
5578N/A */
5578N/A public AttributeCleanupPlugin()
5578N/A {
5578N/A super();
5578N/A }
5578N/A
5578N/A
5578N/A
5578N/A /**
5578N/A * {@inheritDoc}
5578N/A */
5578N/A @Override
5578N/A public ConfigChangeResult applyConfigurationChange(
5578N/A final AttributeCleanupPluginCfg config)
5578N/A {
5578N/A exclusiveLock.lock();
5578N/A try
5578N/A {
5578N/A /* Apply the change, as at this point is has been validated. */
5578N/A this.config = config;
5578N/A
5578N/A attributesToRename = new HashMap<String, String>();
5578N/A for (final String mapping : config.getRenameInboundAttributes())
5578N/A {
5578N/A final int colonPos = mapping.lastIndexOf(":");
5578N/A final String fromAttr = mapping.substring(0, colonPos).trim();
5578N/A final String toAttr = mapping.substring(colonPos + 1).trim();
5578N/A attributesToRename.put(toLowerCase(fromAttr), toLowerCase(toAttr));
5578N/A }
5578N/A
5578N/A attributesToRemove = new HashSet<String>();
5578N/A for (final String attr : config.getRemoveInboundAttributes())
5578N/A {
5578N/A attributesToRemove.add(toLowerCase(attr.trim()));
5578N/A }
5578N/A
5578N/A /* Update was successful, no restart required. */
5578N/A return new ConfigChangeResult(ResultCode.SUCCESS, false);
5578N/A }
5578N/A finally
5578N/A {
5578N/A exclusiveLock.unlock();
5578N/A }
5578N/A }
5578N/A
5578N/A
5578N/A
5578N/A /**
5578N/A * {@inheritDoc}
5578N/A */
5578N/A @Override
5578N/A public PluginResult.PreParse doPreParse(
5578N/A final PreParseAddOperation addOperation)
5578N/A {
5578N/A sharedLock.lock();
5578N/A try
5578N/A {
5578N/A /*
5578N/A * First strip the listed attributes, then rename the ones that remain.
5578N/A */
5578N/A processInboundRemove(addOperation);
5578N/A processInboundRename(addOperation);
5578N/A
5578N/A return PluginResult.PreParse.continueOperationProcessing();
5578N/A }
5578N/A finally
5578N/A {
5578N/A sharedLock.unlock();
5578N/A }
5578N/A }
5578N/A
5578N/A
5578N/A
5578N/A /**
5578N/A * {@inheritDoc}
5578N/A */
5578N/A @Override
5578N/A public PluginResult.PreParse doPreParse(
5578N/A final PreParseModifyOperation modifyOperation)
5578N/A {
5578N/A sharedLock.lock();
5578N/A try
5578N/A {
5578N/A /*
5578N/A * First strip the listed attributes, then rename the ones that remain.
5578N/A */
5578N/A processInboundRemove(modifyOperation);
5578N/A processInboundRename(modifyOperation);
5578N/A
5578N/A /*
5578N/A * If the MODIFY request has been stripped of ALL modifications, stop the
5578N/A * processing and return SUCCESS to the client.
5578N/A */
5578N/A if (modifyOperation.getRawModifications().isEmpty())
5578N/A {
5578N/A if (debugEnabled())
5578N/A {
5578N/A TRACER.debugVerbose("The AttributeCleanupPlugin has eliminated all "
5578N/A + "modifications. The processing should be stopped.");
5578N/A }
5578N/A return PluginResult.PreParse.stopProcessing(ResultCode.SUCCESS, null);
5578N/A }
5578N/A
5578N/A return PluginResult.PreParse.continueOperationProcessing();
5578N/A }
5578N/A finally
5578N/A {
5578N/A sharedLock.unlock();
5578N/A }
5578N/A }
5578N/A
5578N/A
5578N/A
5578N/A /**
5578N/A * {@inheritDoc}
5578N/A */
5578N/A @Override
5578N/A public void finalizePlugin()
5578N/A {
5578N/A /*
5578N/A * It's not essential to take the lock here, but we will anyhow for
5578N/A * consistency with other methods.
5578N/A */
5578N/A exclusiveLock.lock();
5578N/A try
5578N/A {
5578N/A /* Deregister change listeners. */
5578N/A config.removeAttributeCleanupChangeListener(this);
5578N/A }
5578N/A finally
5578N/A {
5578N/A exclusiveLock.unlock();
5578N/A }
5578N/A }
5578N/A
5578N/A
5578N/A
5578N/A /**
5578N/A * {@inheritDoc}
5578N/A */
5578N/A @Override()
5578N/A public void initializePlugin(final Set<PluginType> pluginTypes,
5578N/A final AttributeCleanupPluginCfg configuration) throws ConfigException,
5578N/A InitializationException
5578N/A {
5578N/A /*
5578N/A * The plugin should be invoked only for pre-parse ADD and MODIFY
5578N/A * operations.
5578N/A */
5578N/A for (final PluginType t : pluginTypes)
5578N/A {
5578N/A switch (t)
5578N/A {
5578N/A case PRE_PARSE_ADD:
5578N/A break;
5578N/A case PRE_PARSE_MODIFY:
5578N/A break;
5578N/A default:
5578N/A final Message message = ERR_PLUGIN_ATTR_CLEANUP_INITIALIZE_PLUGIN
5578N/A .get(String.valueOf(t));
5578N/A throw new ConfigException(message);
5578N/A }
5578N/A }
5578N/A
5578N/A /* Verify the current configuration. */
5578N/A final List<Message> messages = new LinkedList<Message>();
5578N/A if (!isConfigurationChangeAcceptable(configuration, messages))
5578N/A {
5578N/A throw new ConfigException(messages.get(0));
5578N/A }
5578N/A
5578N/A /* Register change listeners. */
5578N/A configuration.addAttributeCleanupChangeListener(this);
5578N/A
5578N/A /* Save the configuration. */
5578N/A applyConfigurationChange(configuration);
5578N/A }
5578N/A
5578N/A
5578N/A
5578N/A /**
5578N/A * {@inheritDoc}
5578N/A */
5578N/A @Override
5578N/A public boolean isConfigurationAcceptable(final PluginCfg configuration,
5578N/A final List<Message> unacceptableReasons)
5578N/A {
5578N/A final AttributeCleanupPluginCfg cfg =
5578N/A (AttributeCleanupPluginCfg) configuration;
5578N/A return isConfigurationChangeAcceptable(cfg, unacceptableReasons);
5578N/A }
5578N/A
5578N/A
5578N/A
5578N/A /**
5578N/A * {@inheritDoc}
5578N/A */
5578N/A @Override
5578N/A public boolean isConfigurationChangeAcceptable(
5578N/A final AttributeCleanupPluginCfg config, final List<Message> messages)
5578N/A {
5578N/A /*
5578N/A * The admin framework will ensure that there are no duplicate attributes to
5578N/A * be removed.
5578N/A */
5578N/A boolean isValid = true;
5578N/A
5578N/A /*
5578N/A * Verify that there are no duplicate mappings and that attributes are
5578N/A * renamed to valid attribute types.
5578N/A */
5578N/A final Set<String> fromAttrs = new HashSet<String>();
5578N/A for (final String attr : config.getRenameInboundAttributes())
5578N/A {
5578N/A /*
5578N/A * The format is: from:to where each 'from' and 'to' are attribute
5578N/A * descriptions. The admin framework ensures that the format is correct.
5578N/A */
5578N/A final int colonPos = attr.lastIndexOf(":");
5578N/A final String fromAttr = attr.substring(0, colonPos).trim();
5578N/A final String toAttr = attr.substring(colonPos + 1).trim();
5578N/A
5578N/A /*
5578N/A * Make sure that toAttr is defined within the server, being careful to
5578N/A * ignore attribute options.
5578N/A */
5578N/A final int semicolonPos = toAttr.indexOf(";");
5578N/A final String toAttrType = (semicolonPos < 0)
5578N/A && (semicolonPos < (toAttr.length() - 1)) ? toAttr : toAttr
5578N/A .substring(semicolonPos + 1);
5578N/A
5578N/A if (DirectoryServer.getAttributeType(toLowerCase(toAttrType)) == null)
5578N/A {
5578N/A messages.add(ERR_PLUGIN_ATTR_CLEANUP_ATTRIBUTE_MISSING.get(toAttr));
5578N/A isValid = false;
5578N/A }
5578N/A
5578N/A /*
5578N/A * Check for duplicates.
5578N/A */
5578N/A final String nfromAttr = toLowerCase(fromAttr);
5578N/A if (fromAttrs.contains(nfromAttr))
5578N/A {
5578N/A messages.add(ERR_PLUGIN_ATTR_CLEANUP_DUPLICATE_VALUE.get(fromAttr));
5578N/A isValid = false;
5578N/A }
5578N/A else
5578N/A {
5578N/A fromAttrs.add(nfromAttr);
5578N/A }
5578N/A
5578N/A /*
5578N/A * Check that attribute does not map to itself.
5578N/A */
5578N/A if (nfromAttr.equals(toLowerCase(toAttr)))
5578N/A {
5578N/A messages
5578N/A .add(ERR_PLUGIN_ATTR_CLEANUP_EQUAL_VALUES.get(fromAttr, toAttr));
5578N/A isValid = false;
5578N/A }
5578N/A
5578N/A }
5578N/A
5578N/A return isValid;
5578N/A }
5578N/A
5578N/A
5578N/A
5578N/A /**
5578N/A * Remove the attributes listed in the configuration under
5578N/A * ds-cfg-remove-inbound-attributes from the incoming ADD request.
5578N/A *
5578N/A * @param addOperation
5578N/A * Current ADD operation.
5578N/A */
5578N/A private void processInboundRemove(final PreParseAddOperation addOperation)
5578N/A {
5578N/A final List<RawAttribute> inAttrs = new LinkedList<RawAttribute>(
5578N/A addOperation.getRawAttributes());
5578N/A final ListIterator<RawAttribute> iterator = inAttrs.listIterator();
5578N/A while (iterator.hasNext())
5578N/A {
5578N/A final RawAttribute rawAttr = iterator.next();
5578N/A final String attrName = toLowerCase(rawAttr.getAttributeType().trim());
5578N/A if (attributesToRemove.contains(attrName))
5578N/A {
5578N/A if (debugEnabled())
5578N/A {
5578N/A TRACER.debugVerbose("AttributeCleanupPlugin removing '%s'",
5578N/A rawAttr.getAttributeType());
5578N/A }
5578N/A iterator.remove();
5578N/A }
5578N/A }
5578N/A addOperation.setRawAttributes(inAttrs);
5578N/A }
5578N/A
5578N/A
5578N/A
5578N/A /**
5578N/A * Remove the attributes listed in the configuration under
5578N/A * ds-cfg-remove-inbound-attributes from the incoming MODIFY request.
5578N/A *
5578N/A * @param modifyOperation
5578N/A * Current MODIFY operation.
5578N/A */
5578N/A private void processInboundRemove(
5578N/A final PreParseModifyOperation modifyOperation)
5578N/A {
5578N/A final List<RawModification> rawMods = new LinkedList<RawModification>(
5578N/A modifyOperation.getRawModifications());
5578N/A final ListIterator<RawModification> iterator = rawMods.listIterator();
5578N/A while (iterator.hasNext())
5578N/A {
5578N/A final RawModification rawMod = iterator.next();
5578N/A final RawAttribute rawAttr = rawMod.getAttribute();
5578N/A final String attrName = toLowerCase(rawAttr.getAttributeType().trim());
5578N/A if (attributesToRemove.contains(attrName))
5578N/A {
5578N/A if (debugEnabled())
5578N/A {
5578N/A TRACER.debugVerbose("AttributeCleanupPlugin removing '%s'",
5578N/A rawAttr.getAttributeType());
5578N/A }
5578N/A iterator.remove();
5578N/A }
5578N/A }
5578N/A modifyOperation.setRawModifications(rawMods);
5578N/A }
5578N/A
5578N/A
5578N/A
5578N/A /**
5578N/A * Map the incoming attributes to the local ones.
5578N/A *
5578N/A * @param addOperation
5578N/A * Current ADD operation.
5578N/A */
5578N/A private void processInboundRename(final PreParseAddOperation addOperation)
5578N/A {
5578N/A final List<RawAttribute> inAttrs = new LinkedList<RawAttribute>(
5578N/A addOperation.getRawAttributes());
5578N/A final ListIterator<RawAttribute> iterator = inAttrs.listIterator();
5578N/A while (iterator.hasNext())
5578N/A {
5578N/A final RawAttribute rawAttr = iterator.next();
5578N/A final String fromName = toLowerCase(rawAttr.getAttributeType().trim());
5578N/A final String toName = attributesToRename.get(fromName);
5578N/A if (toName != null)
5578N/A {
5578N/A if (debugEnabled())
5578N/A {
5578N/A TRACER.debugVerbose("AttributeCleanupPlugin renaming '%s' to '%s'",
5578N/A rawAttr.getAttributeType(), toName);
5578N/A }
5578N/A rawAttr.setAttributeType(toName);
5578N/A }
5578N/A }
5578N/A addOperation.setRawAttributes(inAttrs);
5578N/A }
5578N/A
5578N/A
5578N/A
5578N/A /**
5578N/A * Rename the attributes in the incoming MODIFY request to names that exist in
5578N/A * the local schema as defined in the configuration.
5578N/A *
5578N/A * @param modifyOperation
5578N/A * Current MODIFY operation.
5578N/A */
5578N/A private void processInboundRename(
5578N/A final PreParseModifyOperation modifyOperation)
5578N/A {
5578N/A final List<RawModification> rawMods = new LinkedList<RawModification>(
5578N/A modifyOperation.getRawModifications());
5578N/A final ListIterator<RawModification> iterator = rawMods.listIterator();
5578N/A while (iterator.hasNext())
5578N/A {
5578N/A final RawModification rawMod = iterator.next();
5578N/A final RawAttribute rawAttr = rawMod.getAttribute();
5578N/A final String fromName = toLowerCase(rawAttr.getAttributeType().trim());
5578N/A final String toName = attributesToRename.get(fromName);
5578N/A if (toName != null)
5578N/A {
5578N/A if (debugEnabled())
5578N/A {
5578N/A TRACER.debugVerbose("AttributeCleanupPlugin renaming '%s' to '%s'",
5578N/A rawAttr.getAttributeType(), toName);
5578N/A }
5578N/A rawAttr.setAttributeType(toName);
5578N/A }
5578N/A }
5578N/A modifyOperation.setRawModifications(rawMods);
5578N/A }
5578N/A}