001 /* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.plugins; 028 029 030 031 import java.util.ArrayList; 032 import java.util.LinkedHashSet; 033 import java.util.List; 034 import java.util.Set; 035 036 import org.opends.messages.Message; 037 import org.opends.server.admin.server.ConfigurationChangeListener; 038 import org.opends.server.admin.std.meta.PluginCfgDefn; 039 import org.opends.server.admin.std.server.LastModPluginCfg; 040 import org.opends.server.admin.std.server.PluginCfg; 041 import org.opends.server.api.plugin.DirectoryServerPlugin; 042 import org.opends.server.api.plugin.PluginType; 043 import org.opends.server.api.plugin.PluginResult; 044 import org.opends.server.config.ConfigException; 045 import org.opends.server.loggers.debug.DebugTracer; 046 import org.opends.server.types.Attribute; 047 import org.opends.server.types.AttributeType; 048 import org.opends.server.types.AttributeValue; 049 import org.opends.server.types.ByteStringFactory; 050 import org.opends.server.types.ConfigChangeResult; 051 import org.opends.server.types.DebugLogLevel; 052 import org.opends.server.types.DirectoryConfig; 053 import org.opends.server.types.DirectoryException; 054 import org.opends.server.types.DN; 055 import org.opends.server.types.Modification; 056 import org.opends.server.types.ModificationType; 057 import org.opends.server.types.ResultCode; 058 import org.opends.server.types.operation.PreOperationAddOperation; 059 import org.opends.server.types.operation.PreOperationModifyOperation; 060 import org.opends.server.types.operation.PreOperationModifyDNOperation; 061 062 import static org.opends.messages.PluginMessages.*; 063 import static org.opends.server.config.ConfigConstants.*; 064 import static org.opends.server.loggers.debug.DebugLogger.*; 065 import static org.opends.server.util.TimeThread.*; 066 067 068 069 /** 070 * This class implements a Directory Server plugin that will add the 071 * creatorsName and createTimestamp attributes to an entry whenever it is added 072 * to the server, and will add the modifiersName and modifyTimestamp attributes 073 * whenever the entry is modified or renamed. 074 */ 075 public final class LastModPlugin 076 extends DirectoryServerPlugin<LastModPluginCfg> 077 implements ConfigurationChangeListener<LastModPluginCfg> 078 { 079 /** 080 * The tracer object for the debug logger. 081 */ 082 private static final DebugTracer TRACER = getTracer(); 083 084 // The attribute type for the "createTimestamp" attribute. 085 private final AttributeType createTimestampType; 086 087 // The attribute type for the "creatorsName" attribute. 088 private final AttributeType creatorsNameType; 089 090 // The attribute type for the "modifiersName" attribute. 091 private final AttributeType modifiersNameType; 092 093 // The attribute type for the "modifyTimestamp" attribute. 094 private final AttributeType modifyTimestampType; 095 096 // The current configuration for this plugin. 097 private LastModPluginCfg currentConfig; 098 099 100 101 /** 102 * Creates a new instance of this Directory Server plugin. Every plugin must 103 * implement a default constructor (it is the only one that will be used to 104 * create plugins defined in the configuration), and every plugin constructor 105 * must call <CODE>super()</CODE> as its first element. 106 */ 107 public LastModPlugin() 108 { 109 super(); 110 111 112 // Get the attribute types for the attributes that we will use. This needs 113 // to be done in the constructor in order to make the associated variables 114 // "final". 115 createTimestampType = 116 DirectoryConfig.getAttributeType(OP_ATTR_CREATE_TIMESTAMP_LC, true); 117 creatorsNameType = 118 DirectoryConfig.getAttributeType(OP_ATTR_CREATORS_NAME_LC, true); 119 modifiersNameType = 120 DirectoryConfig.getAttributeType(OP_ATTR_MODIFIERS_NAME_LC, true); 121 modifyTimestampType = 122 DirectoryConfig.getAttributeType(OP_ATTR_MODIFY_TIMESTAMP_LC, true); 123 } 124 125 126 127 /** 128 * {@inheritDoc} 129 */ 130 @Override() 131 public final void initializePlugin(Set<PluginType> pluginTypes, 132 LastModPluginCfg configuration) 133 throws ConfigException 134 { 135 currentConfig = configuration; 136 configuration.addLastModChangeListener(this); 137 138 // Make sure that the plugin has been enabled for the appropriate types. 139 for (PluginType t : pluginTypes) 140 { 141 switch (t) 142 { 143 case PRE_OPERATION_ADD: 144 case PRE_OPERATION_MODIFY: 145 case PRE_OPERATION_MODIFY_DN: 146 // These are acceptable. 147 break; 148 149 150 default: 151 Message message = 152 ERR_PLUGIN_LASTMOD_INVALID_PLUGIN_TYPE.get(t.toString()); 153 throw new ConfigException(message); 154 } 155 } 156 } 157 158 159 160 /** 161 * {@inheritDoc} 162 */ 163 @Override() 164 public final void finalizePlugin() 165 { 166 currentConfig.removeLastModChangeListener(this); 167 } 168 169 170 171 /** 172 * {@inheritDoc} 173 */ 174 @Override() 175 public final PluginResult.PreOperation 176 doPreOperation(PreOperationAddOperation addOperation) 177 { 178 // Create the attribute list for the creatorsName attribute, if appropriate. 179 DN creatorDN = addOperation.getAuthorizationDN(); 180 LinkedHashSet<AttributeValue> nameValues = 181 new LinkedHashSet<AttributeValue>(1); 182 if (creatorDN == null) 183 { 184 // This must mean that the operation was performed anonymously. Even so, 185 // we still need to update the creatorsName attribute. 186 nameValues.add(new AttributeValue(creatorsNameType, 187 ByteStringFactory.create())); 188 } 189 else 190 { 191 nameValues.add(new AttributeValue(creatorsNameType, 192 ByteStringFactory.create(creatorDN.toString()))); 193 } 194 Attribute nameAttr = new Attribute(creatorsNameType, OP_ATTR_CREATORS_NAME, 195 nameValues); 196 ArrayList<Attribute> nameList = new ArrayList<Attribute>(1); 197 nameList.add(nameAttr); 198 addOperation.setAttribute(creatorsNameType, nameList); 199 200 201 // Create the attribute list for the createTimestamp attribute. 202 LinkedHashSet<AttributeValue> timeValues = 203 new LinkedHashSet<AttributeValue>(1); 204 timeValues.add(new AttributeValue(createTimestampType, 205 ByteStringFactory.create(getGMTTime()))); 206 207 Attribute timeAttr = new Attribute(createTimestampType, 208 OP_ATTR_CREATE_TIMESTAMP, timeValues); 209 ArrayList<Attribute> timeList = new ArrayList<Attribute>(1); 210 timeList.add(timeAttr); 211 addOperation.setAttribute(createTimestampType, timeList); 212 213 214 // We shouldn't ever need to return a non-success result. 215 return PluginResult.PreOperation.continueOperationProcessing(); 216 } 217 218 219 220 /** 221 * {@inheritDoc} 222 */ 223 @Override() 224 public final PluginResult.PreOperation 225 doPreOperation(PreOperationModifyOperation modifyOperation) 226 { 227 // Create the modifiersName attribute. 228 DN modifierDN = modifyOperation.getAuthorizationDN(); 229 LinkedHashSet<AttributeValue> nameValues = 230 new LinkedHashSet<AttributeValue>(1); 231 if (modifierDN == null) 232 { 233 // This must mean that the operation was performed anonymously. Even so, 234 // we still need to update the modifiersName attribute. 235 nameValues.add(new AttributeValue(modifiersNameType, 236 ByteStringFactory.create())); 237 } 238 else 239 { 240 nameValues.add(new AttributeValue(modifiersNameType, 241 ByteStringFactory.create(modifierDN.toString()))); 242 } 243 Attribute nameAttr = new Attribute(modifiersNameType, 244 OP_ATTR_MODIFIERS_NAME, nameValues); 245 try 246 { 247 modifyOperation.addModification(new Modification(ModificationType.REPLACE, 248 nameAttr, true)); 249 } 250 catch (DirectoryException de) 251 { 252 if (debugEnabled()) 253 { 254 TRACER.debugCaught(DebugLogLevel.ERROR, de); 255 } 256 257 // This should never happen. 258 return PluginResult.PreOperation.stopProcessing( 259 DirectoryConfig.getServerErrorResultCode(), de.getMessageObject()); 260 } 261 262 263 // Create the modifyTimestamp attribute. 264 LinkedHashSet<AttributeValue> timeValues = 265 new LinkedHashSet<AttributeValue>(1); 266 timeValues.add(new AttributeValue(modifyTimestampType, 267 ByteStringFactory.create(getGMTTime()))); 268 269 Attribute timeAttr = new Attribute(modifyTimestampType, 270 OP_ATTR_MODIFY_TIMESTAMP, timeValues); 271 try 272 { 273 modifyOperation.addModification(new Modification(ModificationType.REPLACE, 274 timeAttr, true)); 275 } 276 catch (DirectoryException de) 277 { 278 if (debugEnabled()) 279 { 280 TRACER.debugCaught(DebugLogLevel.ERROR, de); 281 } 282 283 // This should never happen. 284 return PluginResult.PreOperation.stopProcessing( 285 DirectoryConfig.getServerErrorResultCode(), de.getMessageObject()); 286 } 287 288 289 // We shouldn't ever need to return a non-success result. 290 return PluginResult.PreOperation.continueOperationProcessing(); 291 } 292 293 294 295 /** 296 * {@inheritDoc} 297 */ 298 @Override() 299 public final PluginResult.PreOperation 300 doPreOperation(PreOperationModifyDNOperation modifyDNOperation) 301 { 302 // Create the modifiersName attribute. 303 DN modifierDN = modifyDNOperation.getAuthorizationDN(); 304 LinkedHashSet<AttributeValue> nameValues = 305 new LinkedHashSet<AttributeValue>(1); 306 if (modifierDN == null) 307 { 308 // This must mean that the operation was performed anonymously. Even so, 309 // we still need to update the modifiersName attribute. 310 nameValues.add(new AttributeValue(modifiersNameType, 311 ByteStringFactory.create())); 312 } 313 else 314 { 315 nameValues.add(new AttributeValue(modifiersNameType, 316 ByteStringFactory.create(modifierDN.toString()))); 317 } 318 Attribute nameAttr = new Attribute(modifiersNameType, 319 OP_ATTR_MODIFIERS_NAME, nameValues); 320 modifyDNOperation.addModification(new Modification(ModificationType.REPLACE, 321 nameAttr, true)); 322 323 324 // Create the modifyTimestamp attribute. 325 LinkedHashSet<AttributeValue> timeValues = 326 new LinkedHashSet<AttributeValue>(1); 327 timeValues.add(new AttributeValue(modifyTimestampType, 328 ByteStringFactory.create(getGMTTime()))); 329 330 Attribute timeAttr = new Attribute(modifyTimestampType, 331 OP_ATTR_MODIFY_TIMESTAMP, timeValues); 332 modifyDNOperation.addModification(new Modification(ModificationType.REPLACE, 333 timeAttr, true)); 334 335 336 // We shouldn't ever need to return a non-success result. 337 return PluginResult.PreOperation.continueOperationProcessing(); 338 } 339 340 341 342 /** 343 * {@inheritDoc} 344 */ 345 @Override() 346 public boolean isConfigurationAcceptable(PluginCfg configuration, 347 List<Message> unacceptableReasons) 348 { 349 LastModPluginCfg cfg = (LastModPluginCfg) configuration; 350 return isConfigurationChangeAcceptable(cfg, unacceptableReasons); 351 } 352 353 354 355 /** 356 * {@inheritDoc} 357 */ 358 public boolean isConfigurationChangeAcceptable(LastModPluginCfg configuration, 359 List<Message> unacceptableReasons) 360 { 361 boolean configAcceptable = true; 362 363 // Ensure that the set of plugin types contains only pre-operation add, 364 // pre-operation modify, and pre-operation modify DN. 365 for (PluginCfgDefn.PluginType pluginType : configuration.getPluginType()) 366 { 367 switch (pluginType) 368 { 369 case PREOPERATIONADD: 370 case PREOPERATIONMODIFY: 371 case PREOPERATIONMODIFYDN: 372 // These are acceptable. 373 break; 374 375 376 default: 377 Message message = ERR_PLUGIN_LASTMOD_INVALID_PLUGIN_TYPE.get( 378 pluginType.toString()); 379 unacceptableReasons.add(message); 380 configAcceptable = false; 381 } 382 } 383 384 return configAcceptable; 385 } 386 387 388 389 /** 390 * {@inheritDoc} 391 */ 392 public ConfigChangeResult applyConfigurationChange( 393 LastModPluginCfg configuration) 394 { 395 currentConfig = configuration; 396 return new ConfigChangeResult(ResultCode.SUCCESS, false); 397 } 398 } 399