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.core; 028 029 030 031 import java.lang.reflect.Method; 032 import java.util.ArrayList; 033 import java.util.Iterator; 034 import java.util.List; 035 import java.util.concurrent.ConcurrentHashMap; 036 037 import org.opends.server.admin.ClassPropertyDefinition; 038 import org.opends.server.admin.server.ConfigurationAddListener; 039 import org.opends.server.admin.server.ConfigurationChangeListener; 040 import org.opends.server.admin.server.ConfigurationDeleteListener; 041 import org.opends.server.admin.std.meta.AttributeSyntaxCfgDefn; 042 import org.opends.server.admin.std.server.AttributeSyntaxCfg; 043 import org.opends.server.admin.std.server.RootCfg; 044 import org.opends.server.admin.server.ServerManagementContext; 045 import org.opends.server.api.AttributeSyntax; 046 import org.opends.server.config.ConfigException; 047 import org.opends.messages.Message; 048 import org.opends.server.types.AttributeType; 049 import org.opends.server.types.ConfigChangeResult; 050 import org.opends.server.types.DirectoryException; 051 import org.opends.server.types.DN; 052 053 054 import org.opends.server.types.InitializationException; 055 import org.opends.server.types.ResultCode; 056 057 import static org.opends.server.loggers.ErrorLogger.*; 058 import static org.opends.messages.ConfigMessages.*; 059 import static org.opends.server.util.StaticUtils.*; 060 061 062 063 /** 064 * This class defines a utility that will be used to manage the set of attribute 065 * syntaxes defined in the Directory Server. It wil initialize the syntaxes 066 * when the server starts, and then will manage any additions, removals, or 067 * modifications to any syntaxes while the server is running. 068 */ 069 public class AttributeSyntaxConfigManager 070 implements ConfigurationChangeListener<AttributeSyntaxCfg>, 071 ConfigurationAddListener<AttributeSyntaxCfg>, 072 ConfigurationDeleteListener<AttributeSyntaxCfg> 073 074 { 075 // A mapping between the DNs of the config entries and the associated 076 // attribute syntaxes. 077 private ConcurrentHashMap<DN,AttributeSyntax> syntaxes; 078 079 080 081 /** 082 * Creates a new instance of this attribute syntax config manager. 083 */ 084 public AttributeSyntaxConfigManager() 085 { 086 syntaxes = new ConcurrentHashMap<DN,AttributeSyntax>(); 087 } 088 089 090 091 /** 092 * Initializes all attribute syntaxes currently defined in the Directory 093 * Server configuration. This should only be called at Directory Server 094 * startup. 095 * 096 * @throws ConfigException If a configuration problem causes the attribute 097 * syntax initialization process to fail. 098 * 099 * @throws InitializationException If a problem occurs while initializing 100 * the attribute syntaxes that is not 101 * related to the server configuration. 102 */ 103 public void initializeAttributeSyntaxes() 104 throws ConfigException, InitializationException 105 { 106 // Get the root configuration object. 107 ServerManagementContext managementContext = 108 ServerManagementContext.getInstance(); 109 RootCfg rootConfiguration = 110 managementContext.getRootConfiguration(); 111 112 113 // Register as an add and delete listener with the root configuration so we 114 // can be notified if any attribute syntax entries are added or removed. 115 rootConfiguration.addAttributeSyntaxAddListener(this); 116 rootConfiguration.addAttributeSyntaxDeleteListener(this); 117 118 119 //Initialize the existing attribute syntaxes. 120 for (String name : rootConfiguration.listAttributeSyntaxes()) 121 { 122 AttributeSyntaxCfg syntaxConfiguration = 123 rootConfiguration.getAttributeSyntax(name); 124 syntaxConfiguration.addChangeListener(this); 125 126 if (syntaxConfiguration.isEnabled()) 127 { 128 String className = syntaxConfiguration.getJavaClass(); 129 try 130 { 131 AttributeSyntax syntax = loadSyntax(className, syntaxConfiguration, 132 true); 133 134 try 135 { 136 DirectoryServer.registerAttributeSyntax(syntax, false); 137 syntaxes.put(syntaxConfiguration.dn(), syntax); 138 } 139 catch (DirectoryException de) 140 { 141 Message message = WARN_CONFIG_SCHEMA_SYNTAX_CONFLICTING_SYNTAX.get( 142 String.valueOf(syntaxConfiguration.dn()), de.getMessageObject()); 143 logError(message); 144 continue; 145 } 146 } 147 catch (InitializationException ie) 148 { 149 logError(ie.getMessageObject()); 150 continue; 151 } 152 } 153 } 154 } 155 156 157 158 /** 159 * {@inheritDoc} 160 */ 161 public boolean isConfigurationAddAcceptable( 162 AttributeSyntaxCfg configuration, 163 List<Message> unacceptableReasons) 164 { 165 if (configuration.isEnabled()) 166 { 167 // Get the name of the class and make sure we can instantiate it as an 168 // attribute syntax. 169 String className = configuration.getJavaClass(); 170 try 171 { 172 loadSyntax(className, configuration, false); 173 } 174 catch (InitializationException ie) 175 { 176 unacceptableReasons.add(ie.getMessageObject()); 177 return false; 178 } 179 } 180 181 // If we've gotten here, then it's fine. 182 return true; 183 } 184 185 186 187 /** 188 * {@inheritDoc} 189 */ 190 public ConfigChangeResult applyConfigurationAdd( 191 AttributeSyntaxCfg configuration) 192 { 193 ResultCode resultCode = ResultCode.SUCCESS; 194 boolean adminActionRequired = false; 195 ArrayList<Message> messages = new ArrayList<Message>(); 196 197 configuration.addChangeListener(this); 198 199 if (! configuration.isEnabled()) 200 { 201 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 202 } 203 204 AttributeSyntax syntax = null; 205 206 // Get the name of the class and make sure we can instantiate it as an 207 // attribute syntax. 208 String className = configuration.getJavaClass(); 209 try 210 { 211 syntax = loadSyntax(className, configuration, true); 212 213 try 214 { 215 DirectoryServer.registerAttributeSyntax(syntax, false); 216 syntaxes.put(configuration.dn(), syntax); 217 } 218 catch (DirectoryException de) 219 { 220 Message message = WARN_CONFIG_SCHEMA_SYNTAX_CONFLICTING_SYNTAX.get( 221 String.valueOf(configuration.dn()), de.getMessageObject()); 222 messages.add(message); 223 224 if (resultCode == ResultCode.SUCCESS) 225 { 226 resultCode = DirectoryServer.getServerErrorResultCode(); 227 } 228 } 229 } 230 catch (InitializationException ie) 231 { 232 if (resultCode == ResultCode.SUCCESS) 233 { 234 resultCode = DirectoryServer.getServerErrorResultCode(); 235 } 236 237 messages.add(ie.getMessageObject()); 238 } 239 240 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 241 } 242 243 244 245 /** 246 * {@inheritDoc} 247 */ 248 public boolean isConfigurationDeleteAcceptable( 249 AttributeSyntaxCfg configuration, 250 List<Message> unacceptableReasons) 251 { 252 // If the syntax is enabled, then check to see if there are any defined 253 // attribute types that use the syntax. If so, then don't allow it to be 254 // deleted. 255 boolean configAcceptable = true; 256 AttributeSyntax syntax = syntaxes.get(configuration.dn()); 257 if (syntax != null) 258 { 259 String oid = syntax.getOID(); 260 for (AttributeType at : DirectoryServer.getAttributeTypes().values()) 261 { 262 if (oid.equals(at.getSyntaxOID())) 263 { 264 Message message = WARN_CONFIG_SCHEMA_CANNOT_DELETE_SYNTAX_IN_USE.get( 265 syntax.getSyntaxName(), at.getNameOrOID()); 266 unacceptableReasons.add(message); 267 268 configAcceptable = false; 269 } 270 } 271 } 272 273 return configAcceptable; 274 } 275 276 277 278 /** 279 * {@inheritDoc} 280 */ 281 public ConfigChangeResult applyConfigurationDelete( 282 AttributeSyntaxCfg configuration) 283 { 284 ResultCode resultCode = ResultCode.SUCCESS; 285 boolean adminActionRequired = false; 286 ArrayList<Message> messages = new ArrayList<Message>(); 287 288 AttributeSyntax syntax = syntaxes.remove(configuration.dn()); 289 if (syntax != null) 290 { 291 DirectoryServer.deregisterAttributeSyntax(syntax); 292 syntax.finalizeSyntax(); 293 } 294 295 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 296 } 297 298 299 300 /** 301 * {@inheritDoc} 302 */ 303 public boolean isConfigurationChangeAcceptable( 304 AttributeSyntaxCfg configuration, 305 List<Message> unacceptableReasons) 306 { 307 if (configuration.isEnabled()) 308 { 309 // Get the name of the class and make sure we can instantiate it as an 310 // attribute syntax. 311 String className = configuration.getJavaClass(); 312 try 313 { 314 loadSyntax(className, configuration, false); 315 } 316 catch (InitializationException ie) 317 { 318 unacceptableReasons.add(ie.getMessageObject()); 319 return false; 320 } 321 } 322 else 323 { 324 // If the syntax is currently enabled and the change would make it 325 // disabled, then only allow it if the syntax isn't already in use. 326 AttributeSyntax syntax = syntaxes.get(configuration.dn()); 327 if (syntax != null) 328 { 329 String oid = syntax.getOID(); 330 for (AttributeType at : DirectoryServer.getAttributeTypes().values()) 331 { 332 if (oid.equals(at.getSyntaxOID())) 333 { 334 Message message = 335 WARN_CONFIG_SCHEMA_CANNOT_DISABLE_SYNTAX_IN_USE.get( 336 syntax.getSyntaxName(), 337 at.getNameOrOID()); 338 unacceptableReasons.add(message); 339 return false; 340 } 341 } 342 } 343 } 344 345 // If we've gotten here, then it's fine. 346 return true; 347 } 348 349 350 351 /** 352 * {@inheritDoc} 353 */ 354 public ConfigChangeResult applyConfigurationChange( 355 AttributeSyntaxCfg configuration) 356 { 357 ResultCode resultCode = ResultCode.SUCCESS; 358 boolean adminActionRequired = false; 359 ArrayList<Message> messages = new ArrayList<Message>(); 360 361 362 // Get the existing syntax if it's already enabled. 363 AttributeSyntax existingSyntax = syntaxes.get(configuration.dn()); 364 365 366 // If the new configuration has the syntax disabled, then disable it if it 367 // is enabled, or do nothing if it's already disabled. 368 if (! configuration.isEnabled()) 369 { 370 if (existingSyntax != null) 371 { 372 DirectoryServer.deregisterAttributeSyntax(existingSyntax); 373 374 AttributeSyntax syntax = syntaxes.remove(configuration.dn()); 375 if (syntax != null) 376 { 377 syntax.finalizeSyntax(); 378 } 379 } 380 381 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 382 } 383 384 385 // Get the class for the attribute syntax. If the syntax is already 386 // enabled, then we shouldn't do anything with it although if the class has 387 // changed then we'll at least need to indicate that administrative action 388 // is required. If the syntax is disabled, then instantiate the class and 389 // initialize and register it as an attribute syntax. 390 String className = configuration.getJavaClass(); 391 if (existingSyntax != null) 392 { 393 if (! className.equals(existingSyntax.getClass().getName())) 394 { 395 adminActionRequired = true; 396 } 397 398 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 399 } 400 401 AttributeSyntax syntax = null; 402 try 403 { 404 syntax = loadSyntax(className, configuration, true); 405 406 try 407 { 408 DirectoryServer.registerAttributeSyntax(syntax, false); 409 syntaxes.put(configuration.dn(), syntax); 410 } 411 catch (DirectoryException de) 412 { 413 Message message = WARN_CONFIG_SCHEMA_SYNTAX_CONFLICTING_SYNTAX.get( 414 String.valueOf(configuration.dn()), 415 de.getMessageObject()); 416 messages.add(message); 417 418 if (resultCode == ResultCode.SUCCESS) 419 { 420 resultCode = DirectoryServer.getServerErrorResultCode(); 421 } 422 } 423 } 424 catch (InitializationException ie) 425 { 426 if (resultCode == ResultCode.SUCCESS) 427 { 428 resultCode = DirectoryServer.getServerErrorResultCode(); 429 } 430 431 messages.add(ie.getMessageObject()); 432 } 433 434 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 435 } 436 437 438 439 /** 440 * Loads the specified class, instantiates it as an attribute syntax, and 441 * optionally initializes that instance. 442 * 443 * @param className The fully-qualified name of the attribute syntax 444 * class to load, instantiate, and initialize. 445 * @param configuration The configuration to use to initialize the attribute 446 * syntax. It should not be {@code null}. 447 * @param initialize Indicates whether the attribute syntax instance 448 * should be initialized. 449 * 450 * @return The possibly initialized attribute syntax. 451 * 452 * @throws InitializationException If a problem occurred while attempting to 453 * initialize the attribute syntax. 454 */ 455 private AttributeSyntax loadSyntax(String className, 456 AttributeSyntaxCfg configuration, 457 boolean initialize) 458 throws InitializationException 459 { 460 try 461 { 462 AttributeSyntaxCfgDefn definition = 463 AttributeSyntaxCfgDefn.getInstance(); 464 ClassPropertyDefinition propertyDefinition = 465 definition.getJavaClassPropertyDefinition(); 466 Class<? extends AttributeSyntax> syntaxClass = 467 propertyDefinition.loadClass(className, AttributeSyntax.class); 468 AttributeSyntax syntax = syntaxClass.newInstance(); 469 470 if (initialize) 471 { 472 Method method = syntax.getClass().getMethod("initializeSyntax", 473 configuration.configurationClass()); 474 method.invoke(syntax, configuration); 475 } 476 else 477 { 478 Method method = syntax.getClass().getMethod("isConfigurationAcceptable", 479 AttributeSyntaxCfg.class, 480 List.class); 481 482 List<Message> unacceptableReasons = new ArrayList<Message>(); 483 Boolean acceptable = (Boolean) method.invoke(syntax, configuration, 484 unacceptableReasons); 485 if (! acceptable) 486 { 487 StringBuilder buffer = new StringBuilder(); 488 if (! unacceptableReasons.isEmpty()) 489 { 490 Iterator<Message> iterator = unacceptableReasons.iterator(); 491 buffer.append(iterator.next()); 492 while (iterator.hasNext()) 493 { 494 buffer.append(". "); 495 buffer.append(iterator.next()); 496 } 497 } 498 499 Message message = ERR_CONFIG_SCHEMA_SYNTAX_CONFIG_NOT_ACCEPTABLE.get( 500 String.valueOf(configuration.dn()), buffer.toString()); 501 throw new InitializationException(message); 502 } 503 } 504 505 return syntax; 506 } 507 catch (Exception e) 508 { 509 Message message = ERR_CONFIG_SCHEMA_SYNTAX_CANNOT_INITIALIZE. 510 get(className, String.valueOf(configuration.dn()), 511 stackTraceToSingleLineString(e)); 512 throw new InitializationException(message, e); 513 } 514 } 515 } 516