001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2006-2008 Sun Microsystems, Inc. 015 * Portions Copyright 2014-2016 ForgeRock AS. 016 */ 017package org.opends.server.plugins; 018 019import static org.forgerock.opendj.ldap.schema.CoreSchema.*; 020import static org.opends.messages.PluginMessages.*; 021 022import java.util.List; 023import java.util.Set; 024import java.util.UUID; 025 026import org.forgerock.i18n.LocalizableMessage; 027import org.forgerock.opendj.config.server.ConfigChangeResult; 028import org.forgerock.opendj.config.server.ConfigException; 029import org.forgerock.opendj.config.server.ConfigurationChangeListener; 030import org.forgerock.opendj.ldap.schema.AttributeType; 031import org.forgerock.opendj.server.config.meta.PluginCfgDefn; 032import org.forgerock.opendj.server.config.server.EntryUUIDPluginCfg; 033import org.forgerock.opendj.server.config.server.PluginCfg; 034import org.opends.server.api.plugin.DirectoryServerPlugin; 035import org.opends.server.api.plugin.PluginResult; 036import org.opends.server.api.plugin.PluginType; 037import org.opends.server.types.Attribute; 038import org.opends.server.types.Attributes; 039import org.opends.server.types.Entry; 040import org.opends.server.types.LDIFImportConfig; 041import org.opends.server.types.operation.PreOperationAddOperation; 042 043/** 044 * This class implements a Directory Server plugin that will add the entryUUID 045 * attribute to an entry whenever it is added or imported as per RFC 4530. For 046 * entries added over LDAP, the entryUUID will be based on a semi-random UUID 047 * (which is still guaranteed to be unique). For entries imported from LDIF, 048 * the UUID will be constructed from the entry DN using a repeatable algorithm. 049 * This will ensure that LDIF files imported in parallel across multiple systems 050 * will have identical entryUUID values. 051 */ 052public final class EntryUUIDPlugin 053 extends DirectoryServerPlugin<EntryUUIDPluginCfg> 054 implements ConfigurationChangeListener<EntryUUIDPluginCfg> 055{ 056 /** The attribute type for the "entryUUID" attribute. */ 057 private static final AttributeType entryUUIDType = getEntryUUIDAttributeType(); 058 /** The current configuration for this plugin. */ 059 private EntryUUIDPluginCfg currentConfig; 060 061 /** Mandatory default constructor of this Directory Server plugin. */ 062 public EntryUUIDPlugin() 063 { 064 super(); 065 } 066 067 @Override 068 public final void initializePlugin(Set<PluginType> pluginTypes, 069 EntryUUIDPluginCfg configuration) 070 throws ConfigException 071 { 072 currentConfig = configuration; 073 configuration.addEntryUUIDChangeListener(this); 074 075 // Make sure that the plugin has been enabled for the appropriate types. 076 for (PluginType t : pluginTypes) 077 { 078 switch (t) 079 { 080 case LDIF_IMPORT: 081 case PRE_OPERATION_ADD: 082 // These are acceptable. 083 break; 084 085 default: 086 throw new ConfigException(ERR_PLUGIN_ENTRYUUID_INVALID_PLUGIN_TYPE.get(t)); 087 } 088 } 089 } 090 091 @Override 092 public final void finalizePlugin() 093 { 094 currentConfig.removeEntryUUIDChangeListener(this); 095 } 096 097 @Override 098 public final PluginResult.ImportLDIF 099 doLDIFImport(LDIFImportConfig importConfig, Entry entry) 100 { 101 // See if the entry being imported already contains an entryUUID attribute. 102 // If so, then leave it alone. 103 List<Attribute> uuidList = entry.getAttribute(entryUUIDType); 104 if (!uuidList.isEmpty()) 105 { 106 return PluginResult.ImportLDIF.continueEntryProcessing(); 107 } 108 109 // Construct a new UUID. In order to make sure that UUIDs are consistent 110 // when the same LDIF is generated on multiple servers, we'll base the UUID 111 // on the byte representation of the normalized DN. 112 entry.putAttribute(entryUUIDType, toAttributeList(entry.getName().toUUID())); 113 114 // We shouldn't ever need to return a non-success result. 115 return PluginResult.ImportLDIF.continueEntryProcessing(); 116 } 117 118 @Override 119 public final PluginResult.PreOperation 120 doPreOperation(PreOperationAddOperation addOperation) 121 { 122 // See if the entry being added already contains an entryUUID attribute. 123 // It shouldn't, since it's NO-USER-MODIFICATION, but if it does then leave 124 // it alone. 125 List<Attribute> uuidList = addOperation.getOperationalAttributes().get(entryUUIDType); 126 if (uuidList != null) 127 { 128 return PluginResult.PreOperation.continueOperationProcessing(); 129 } 130 131 // Construct a new random UUID. 132 addOperation.setAttribute(entryUUIDType, toAttributeList(UUID.randomUUID())); 133 return PluginResult.PreOperation.continueOperationProcessing(); 134 } 135 136 private List<Attribute> toAttributeList(UUID uuid) 137 { 138 return Attributes.createAsList(entryUUIDType, uuid.toString()); 139 } 140 141 @Override 142 public boolean isConfigurationAcceptable(PluginCfg configuration, 143 List<LocalizableMessage> unacceptableReasons) 144 { 145 EntryUUIDPluginCfg cfg = (EntryUUIDPluginCfg) configuration; 146 return isConfigurationChangeAcceptable(cfg, unacceptableReasons); 147 } 148 149 @Override 150 public boolean isConfigurationChangeAcceptable( 151 EntryUUIDPluginCfg configuration, 152 List<LocalizableMessage> unacceptableReasons) 153 { 154 boolean configAcceptable = true; 155 156 // Ensure that the set of plugin types contains only LDIF import and 157 // pre-operation add. 158 for (PluginCfgDefn.PluginType pluginType : configuration.getPluginType()) 159 { 160 switch (pluginType) 161 { 162 case LDIFIMPORT: 163 case PREOPERATIONADD: 164 // These are acceptable. 165 break; 166 167 default: 168 unacceptableReasons.add(ERR_PLUGIN_ENTRYUUID_INVALID_PLUGIN_TYPE.get(pluginType)); 169 configAcceptable = false; 170 } 171 } 172 173 return configAcceptable; 174 } 175 176 @Override 177 public ConfigChangeResult applyConfigurationChange( 178 EntryUUIDPluginCfg configuration) 179 { 180 currentConfig = configuration; 181 return new ConfigChangeResult(); 182 } 183}