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