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