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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2008 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027package org.opends.server.core; 028 029import static org.opends.messages.ConfigMessages.*; 030import static org.opends.server.util.StaticUtils.*; 031 032import java.util.ArrayList; 033import java.util.List; 034import java.util.concurrent.ConcurrentHashMap; 035 036import org.forgerock.i18n.LocalizableMessage; 037import org.forgerock.i18n.slf4j.LocalizedLogger; 038import org.forgerock.opendj.config.server.ConfigException; 039import org.forgerock.util.Utils; 040import org.opends.server.admin.ClassPropertyDefinition; 041import org.opends.server.admin.server.ConfigurationAddListener; 042import org.opends.server.admin.server.ConfigurationChangeListener; 043import org.opends.server.admin.server.ConfigurationDeleteListener; 044import org.opends.server.admin.server.ServerManagementContext; 045import org.opends.server.admin.std.meta.AttributeSyntaxCfgDefn; 046import org.opends.server.admin.std.server.AttributeSyntaxCfg; 047import org.opends.server.admin.std.server.RootCfg; 048import org.opends.server.api.AttributeSyntax; 049import org.opends.server.types.AttributeType; 050import org.forgerock.opendj.config.server.ConfigChangeResult; 051import org.forgerock.opendj.ldap.schema.Syntax; 052import org.opends.server.types.DN; 053import org.opends.server.types.DirectoryException; 054import org.opends.server.types.InitializationException; 055 056/** 057 * This class defines a utility that will be used to manage the set of attribute 058 * syntaxes defined in the Directory Server. It will initialize the syntaxes 059 * when the server starts, and then will manage any additions, removals, or 060 * modifications to any syntaxes while the server is running. 061 */ 062public class AttributeSyntaxConfigManager 063 implements ConfigurationChangeListener<AttributeSyntaxCfg>, 064 ConfigurationAddListener<AttributeSyntaxCfg>, 065 ConfigurationDeleteListener<AttributeSyntaxCfg> 066 067{ 068 069 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 070 071 /** 072 * A mapping between the DNs of the config entries and the associated 073 * attribute syntaxes. 074 */ 075 private ConcurrentHashMap<DN,AttributeSyntax> syntaxes; 076 077 private final ServerContext serverContext; 078 079 /** 080 * Creates a new instance of this attribute syntax config manager. 081 * 082 * @param serverContext 083 * The server context, that contains the schema. 084 */ 085 public AttributeSyntaxConfigManager(final ServerContext serverContext) 086 { 087 this.serverContext = serverContext; 088 syntaxes = new ConcurrentHashMap<>(); 089 } 090 091 092 093 /** 094 * Initializes all attribute syntaxes currently defined in the Directory 095 * Server configuration. This should only be called at Directory Server 096 * startup. 097 * 098 * @throws ConfigException If a configuration problem causes the attribute 099 * syntax initialization process to fail. 100 * 101 * @throws InitializationException If a problem occurs while initializing 102 * the attribute syntaxes that is not 103 * related to the server configuration. 104 */ 105 public void initializeAttributeSyntaxes() 106 throws ConfigException, InitializationException 107 { 108 // Get the root configuration object. 109 ServerManagementContext managementContext = 110 ServerManagementContext.getInstance(); 111 RootCfg rootConfiguration = 112 managementContext.getRootConfiguration(); 113 114 115 // Register as an add and delete listener with the root configuration so we 116 // can be notified if any attribute syntax entries are added or removed. 117 rootConfiguration.addAttributeSyntaxAddListener(this); 118 rootConfiguration.addAttributeSyntaxDeleteListener(this); 119 120 121 //Initialize the existing attribute syntaxes. 122 for (String name : rootConfiguration.listAttributeSyntaxes()) 123 { 124 AttributeSyntaxCfg syntaxConfiguration = 125 rootConfiguration.getAttributeSyntax(name); 126 syntaxConfiguration.addChangeListener(this); 127 128 if (syntaxConfiguration.isEnabled()) 129 { 130 String className = syntaxConfiguration.getJavaClass(); 131 try 132 { 133 AttributeSyntax<?> syntax = loadSyntax(className, syntaxConfiguration, true); 134 try 135 { 136 Syntax sdkSyntax = syntax.getSDKSyntax(serverContext.getSchemaNG()); 137 serverContext.getSchema().registerSyntax(sdkSyntax, false); 138 syntaxes.put(syntaxConfiguration.dn(), syntax); 139 } 140 catch (DirectoryException de) 141 { 142 logger.warn(WARN_CONFIG_SCHEMA_SYNTAX_CONFLICTING_SYNTAX, syntaxConfiguration.dn(), de.getMessageObject()); 143 continue; 144 } 145 } 146 catch (InitializationException ie) 147 { 148 logger.error(ie.getMessageObject()); 149 continue; 150 } 151 } 152 } 153 } 154 155 156 157 /** {@inheritDoc} */ 158 @Override 159 public boolean isConfigurationAddAcceptable( 160 AttributeSyntaxCfg configuration, 161 List<LocalizableMessage> unacceptableReasons) 162 { 163 if (configuration.isEnabled()) 164 { 165 // Get the name of the class and make sure we can instantiate it as an 166 // attribute syntax. 167 String className = configuration.getJavaClass(); 168 try 169 { 170 loadSyntax(className, configuration, false); 171 } 172 catch (InitializationException ie) 173 { 174 unacceptableReasons.add(ie.getMessageObject()); 175 return false; 176 } 177 } 178 179 // If we've gotten here, then it's fine. 180 return true; 181 } 182 183 184 185 /** {@inheritDoc} */ 186 @Override 187 public ConfigChangeResult applyConfigurationAdd( 188 AttributeSyntaxCfg configuration) 189 { 190 final ConfigChangeResult ccr = new ConfigChangeResult(); 191 192 configuration.addChangeListener(this); 193 194 if (! configuration.isEnabled()) 195 { 196 return ccr; 197 } 198 199 AttributeSyntax syntax = null; 200 201 // Get the name of the class and make sure we can instantiate it as an 202 // attribute syntax. 203 String className = configuration.getJavaClass(); 204 try 205 { 206 syntax = loadSyntax(className, configuration, true); 207 208 try 209 { 210 Syntax sdkSyntax = syntax.getSDKSyntax(serverContext.getSchemaNG()); 211 serverContext.getSchema().registerSyntax(sdkSyntax, false); 212 syntaxes.put(configuration.dn(), syntax); 213 } 214 catch (DirectoryException de) 215 { 216 ccr.addMessage(WARN_CONFIG_SCHEMA_SYNTAX_CONFLICTING_SYNTAX.get( 217 configuration.dn(), de.getMessageObject())); 218 ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode()); 219 } 220 } 221 catch (InitializationException ie) 222 { 223 ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode()); 224 ccr.addMessage(ie.getMessageObject()); 225 } 226 227 return ccr; 228 } 229 230 231 232 /** {@inheritDoc} */ 233 @Override 234 public boolean isConfigurationDeleteAcceptable( 235 AttributeSyntaxCfg configuration, 236 List<LocalizableMessage> unacceptableReasons) 237 { 238 // If the syntax is enabled, then check to see if there are any defined 239 // attribute types that use the syntax. If so, then don't allow it to be 240 // deleted. 241 boolean configAcceptable = true; 242 AttributeSyntax syntax = syntaxes.get(configuration.dn()); 243 if (syntax != null) 244 { 245 String oid = syntax.getOID(); 246 for (AttributeType at : DirectoryServer.getAttributeTypes().values()) 247 { 248 if (oid.equals(at.getSyntax().getOID())) 249 { 250 LocalizableMessage message = WARN_CONFIG_SCHEMA_CANNOT_DELETE_SYNTAX_IN_USE.get( 251 syntax.getName(), at.getNameOrOID()); 252 unacceptableReasons.add(message); 253 254 configAcceptable = false; 255 } 256 } 257 } 258 259 return configAcceptable; 260 } 261 262 263 264 /** {@inheritDoc} */ 265 @Override 266 public ConfigChangeResult applyConfigurationDelete( 267 AttributeSyntaxCfg configuration) 268 { 269 final ConfigChangeResult ccr = new ConfigChangeResult(); 270 271 AttributeSyntax<?> syntax = syntaxes.remove(configuration.dn()); 272 if (syntax != null) 273 { 274 Syntax sdkSyntax = syntax.getSDKSyntax(serverContext.getSchemaNG()); 275 serverContext.getSchema().deregisterSyntax(sdkSyntax); 276 syntax.finalizeSyntax(); 277 } 278 279 return ccr; 280 } 281 282 283 284 /** {@inheritDoc} */ 285 @Override 286 public boolean isConfigurationChangeAcceptable( 287 AttributeSyntaxCfg configuration, 288 List<LocalizableMessage> unacceptableReasons) 289 { 290 if (configuration.isEnabled()) 291 { 292 // Get the name of the class and make sure we can instantiate it as an 293 // attribute syntax. 294 String className = configuration.getJavaClass(); 295 try 296 { 297 loadSyntax(className, configuration, false); 298 } 299 catch (InitializationException ie) 300 { 301 unacceptableReasons.add(ie.getMessageObject()); 302 return false; 303 } 304 } 305 else 306 { 307 // If the syntax is currently enabled and the change would make it 308 // disabled, then only allow it if the syntax isn't already in use. 309 AttributeSyntax<?> syntax = syntaxes.get(configuration.dn()); 310 if (syntax != null) 311 { 312 String oid = syntax.getOID(); 313 for (AttributeType at : DirectoryServer.getAttributeTypes().values()) 314 { 315 if (oid.equals(at.getSyntax().getOID())) 316 { 317 LocalizableMessage message = 318 WARN_CONFIG_SCHEMA_CANNOT_DISABLE_SYNTAX_IN_USE.get(syntax.getName(), at.getNameOrOID()); 319 unacceptableReasons.add(message); 320 return false; 321 } 322 } 323 } 324 } 325 326 // If we've gotten here, then it's fine. 327 return true; 328 } 329 330 331 332 /** {@inheritDoc} */ 333 @Override 334 public ConfigChangeResult applyConfigurationChange(AttributeSyntaxCfg configuration) 335 { 336 final ConfigChangeResult ccr = new ConfigChangeResult(); 337 338 339 // Get the existing syntax if it's already enabled. 340 AttributeSyntax<?> existingSyntax = syntaxes.get(configuration.dn()); 341 342 343 // If the new configuration has the syntax disabled, then disable it if it 344 // is enabled, or do nothing if it's already disabled. 345 if (! configuration.isEnabled()) 346 { 347 if (existingSyntax != null) 348 { 349 Syntax sdkSyntax = existingSyntax.getSDKSyntax(serverContext.getSchemaNG()); 350 serverContext.getSchema().deregisterSyntax(sdkSyntax); 351 AttributeSyntax<?> syntax = syntaxes.remove(configuration.dn()); 352 if (syntax != null) 353 { 354 syntax.finalizeSyntax(); 355 } 356 } 357 358 return ccr; 359 } 360 361 362 // Get the class for the attribute syntax. If the syntax is already 363 // enabled, then we shouldn't do anything with it although if the class has 364 // changed then we'll at least need to indicate that administrative action 365 // is required. If the syntax is disabled, then instantiate the class and 366 // initialize and register it as an attribute syntax. 367 String className = configuration.getJavaClass(); 368 if (existingSyntax != null) 369 { 370 if (! className.equals(existingSyntax.getClass().getName())) 371 { 372 ccr.setAdminActionRequired(true); 373 } 374 375 return ccr; 376 } 377 378 AttributeSyntax<?> syntax = null; 379 try 380 { 381 syntax = loadSyntax(className, configuration, true); 382 383 try 384 { 385 Syntax sdkSyntax = syntax.getSDKSyntax(serverContext.getSchemaNG()); 386 serverContext.getSchema().registerSyntax(sdkSyntax, false); 387 syntaxes.put(configuration.dn(), syntax); 388 } 389 catch (DirectoryException de) 390 { 391 ccr.addMessage(WARN_CONFIG_SCHEMA_SYNTAX_CONFLICTING_SYNTAX.get( 392 configuration.dn(), de.getMessageObject())); 393 ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode()); 394 } 395 } 396 catch (InitializationException ie) 397 { 398 ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode()); 399 ccr.addMessage(ie.getMessageObject()); 400 } 401 402 return ccr; 403 } 404 405 406 407 /** 408 * Loads the specified class, instantiates it as an attribute syntax, and 409 * optionally initializes that instance. 410 * 411 * @param className The fully-qualified name of the attribute syntax 412 * class to load, instantiate, and initialize. 413 * @param configuration The configuration to use to initialize the attribute 414 * syntax. It should not be {@code null}. 415 * @param initialize Indicates whether the attribute syntax instance 416 * should be initialized. 417 * 418 * @return The possibly initialized attribute syntax. 419 * 420 * @throws InitializationException If a problem occurred while attempting to 421 * initialize the attribute syntax. 422 */ 423 private AttributeSyntax<?> loadSyntax(String className, 424 AttributeSyntaxCfg configuration, 425 boolean initialize) 426 throws InitializationException 427 { 428 try 429 { 430 AttributeSyntaxCfgDefn definition = 431 AttributeSyntaxCfgDefn.getInstance(); 432 ClassPropertyDefinition propertyDefinition = 433 definition.getJavaClassPropertyDefinition(); 434 Class<? extends AttributeSyntax> syntaxClass = 435 propertyDefinition.loadClass(className, AttributeSyntax.class); 436 AttributeSyntax syntax = syntaxClass.newInstance(); 437 438 if (initialize) 439 { 440 syntax.initializeSyntax(configuration, serverContext); 441 } 442 else 443 { 444 List<LocalizableMessage> unacceptableReasons = new ArrayList<>(); 445 if (!syntax.isConfigurationAcceptable(configuration, unacceptableReasons)) 446 { 447 String reasons = Utils.joinAsString(". ", unacceptableReasons); 448 throw new InitializationException( 449 ERR_CONFIG_SCHEMA_SYNTAX_CONFIG_NOT_ACCEPTABLE.get(configuration.dn(), reasons)); 450 } 451 } 452 453 return syntax; 454 } 455 catch (Exception e) 456 { 457 LocalizableMessage message = ERR_CONFIG_SCHEMA_SYNTAX_CANNOT_INITIALIZE. 458 get(className, configuration.dn(), stackTraceToSingleLineString(e)); 459 throw new InitializationException(message, e); 460 } 461 } 462} 463