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 2007-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.core; 028 import org.opends.messages.Message; 029 030 031 032 import java.lang.reflect.Method; 033 import java.util.ArrayList; 034 import java.util.Iterator; 035 import java.util.List; 036 import java.util.LinkedHashSet; 037 import java.util.concurrent.ConcurrentHashMap; 038 039 import org.opends.server.admin.ClassPropertyDefinition; 040 import org.opends.server.admin.server.ConfigurationAddListener; 041 import org.opends.server.admin.server.ConfigurationChangeListener; 042 import org.opends.server.admin.server.ConfigurationDeleteListener; 043 import org.opends.server.admin.std.meta.VirtualAttributeCfgDefn; 044 import org.opends.server.admin.std.server.VirtualAttributeCfg; 045 import org.opends.server.admin.std.server.RootCfg; 046 import org.opends.server.admin.server.ServerManagementContext; 047 import org.opends.server.api.VirtualAttributeProvider; 048 import org.opends.server.config.ConfigException; 049 import org.opends.server.types.ConfigChangeResult; 050 import org.opends.server.types.DebugLogLevel; 051 import org.opends.server.types.DirectoryException; 052 import org.opends.server.types.DN; 053 054 055 import org.opends.server.types.InitializationException; 056 import org.opends.server.types.ResultCode; 057 import org.opends.server.types.SearchFilter; 058 import org.opends.server.types.VirtualAttributeRule; 059 060 import static org.opends.server.loggers.debug.DebugLogger.*; 061 import org.opends.server.loggers.debug.DebugTracer; 062 import org.opends.server.loggers.ErrorLogger; 063 import static org.opends.messages.ConfigMessages.*; 064 065 import static org.opends.server.util.StaticUtils.*; 066 067 068 069 /** 070 * This class defines a utility that will be used to manage the set of 071 * virtual attribute providers defined in the Directory Server. It will 072 * initialize the providers when the server starts, and then will manage any 073 * additions, removals, or modifications to any virtual attribute providers 074 * while the server is running. 075 */ 076 public class VirtualAttributeConfigManager 077 implements ConfigurationChangeListener<VirtualAttributeCfg>, 078 ConfigurationAddListener<VirtualAttributeCfg>, 079 ConfigurationDeleteListener<VirtualAttributeCfg> 080 { 081 /** 082 * The tracer object for the debug logger. 083 */ 084 private static final DebugTracer TRACER = getTracer(); 085 086 // A mapping between the DNs of the config entries and the associated 087 // virtual attribute rules. 088 private ConcurrentHashMap<DN,VirtualAttributeRule> rules; 089 090 091 092 /** 093 * Creates a new instance of this virtual attribute config manager. 094 */ 095 public VirtualAttributeConfigManager() 096 { 097 rules = new ConcurrentHashMap<DN,VirtualAttributeRule>(); 098 } 099 100 101 102 /** 103 * Initializes all virtual attribute providers currently defined in the 104 * Directory Server configuration. This should only be called at Directory 105 * Server startup. 106 * 107 * @throws ConfigException If a configuration problem causes the virtual 108 * attribute provider initialization process to 109 * fail. 110 * 111 * @throws InitializationException If a problem occurs while initializing 112 * the virtual attribute providers that is 113 * not related to the server configuration. 114 */ 115 public void initializeVirtualAttributes() 116 throws ConfigException, InitializationException 117 { 118 // Get the root configuration object. 119 ServerManagementContext managementContext = 120 ServerManagementContext.getInstance(); 121 RootCfg rootConfiguration = 122 managementContext.getRootConfiguration(); 123 124 125 // Register as an add and delete listener with the root configuration so we 126 // can be notified if any virtual attribute provider entries are added or 127 // removed. 128 rootConfiguration.addVirtualAttributeAddListener(this); 129 rootConfiguration.addVirtualAttributeDeleteListener(this); 130 131 132 //Initialize the existing virtual attribute providers. 133 for (String providerName : rootConfiguration.listVirtualAttributes()) 134 { 135 VirtualAttributeCfg cfg = 136 rootConfiguration.getVirtualAttribute(providerName); 137 cfg.addChangeListener(this); 138 139 if (cfg.isEnabled()) 140 { 141 String className = cfg.getJavaClass(); 142 try 143 { 144 VirtualAttributeProvider<? extends VirtualAttributeCfg> provider = 145 loadProvider(className, cfg, true); 146 147 LinkedHashSet<SearchFilter> filters = 148 new LinkedHashSet<SearchFilter>(); 149 for (String filterString : cfg.getFilter()) 150 { 151 try 152 { 153 filters.add(SearchFilter.createFilterFromString(filterString)); 154 } 155 catch (DirectoryException de) 156 { 157 if (debugEnabled()) 158 { 159 TRACER.debugCaught(DebugLogLevel.ERROR, de); 160 } 161 162 Message message = ERR_CONFIG_VATTR_INVALID_SEARCH_FILTER.get( 163 filterString, String.valueOf(cfg.dn()), 164 de.getMessageObject()); 165 throw new ConfigException(message, de); 166 } 167 } 168 169 if (cfg.getAttributeType().isSingleValue()) 170 { 171 if (provider.isMultiValued()) 172 { 173 Message message = ERR_CONFIG_VATTR_SV_TYPE_WITH_MV_PROVIDER. 174 get(String.valueOf(cfg.dn()), 175 cfg.getAttributeType().getNameOrOID(), className); 176 throw new ConfigException(message); 177 } 178 else if (cfg.getConflictBehavior() == 179 VirtualAttributeCfgDefn.ConflictBehavior. 180 MERGE_REAL_AND_VIRTUAL) 181 { 182 Message message = ERR_CONFIG_VATTR_SV_TYPE_WITH_MERGE_VALUES. 183 get(String.valueOf(cfg.dn()), 184 cfg.getAttributeType().getNameOrOID()); 185 throw new ConfigException(message); 186 } 187 } 188 189 VirtualAttributeRule rule = 190 new VirtualAttributeRule(cfg.getAttributeType(), provider, 191 cfg.getBaseDN(), cfg.getGroupDN(), 192 filters, cfg.getConflictBehavior()); 193 rules.put(cfg.dn(), rule); 194 DirectoryServer.registerVirtualAttribute(rule); 195 } 196 catch (InitializationException ie) 197 { 198 ErrorLogger.logError(ie.getMessageObject()); 199 continue; 200 } 201 } 202 } 203 } 204 205 206 207 /** 208 * {@inheritDoc} 209 */ 210 public boolean isConfigurationAddAcceptable( 211 VirtualAttributeCfg configuration, 212 List<Message> unacceptableReasons) 213 { 214 if (configuration.isEnabled()) 215 { 216 // Get the name of the class and make sure we can instantiate it as a 217 // virtual attribute provider. 218 String className = configuration.getJavaClass(); 219 try 220 { 221 loadProvider(className, configuration, false); 222 } 223 catch (InitializationException ie) 224 { 225 unacceptableReasons.add(ie.getMessageObject()); 226 return false; 227 } 228 } 229 230 // If there were any search filters provided, then make sure they are all 231 // valid. 232 for (String filterString : configuration.getFilter()) 233 { 234 try 235 { 236 SearchFilter.createFilterFromString(filterString); 237 } 238 catch (DirectoryException de) 239 { 240 if (debugEnabled()) 241 { 242 TRACER.debugCaught(DebugLogLevel.ERROR, de); 243 } 244 245 Message message = ERR_CONFIG_VATTR_INVALID_SEARCH_FILTER.get( 246 filterString, 247 String.valueOf(configuration.dn()), 248 de.getMessageObject()); 249 unacceptableReasons.add(message); 250 return false; 251 } 252 } 253 254 // If we've gotten here, then it's fine. 255 return true; 256 } 257 258 259 260 /** 261 * {@inheritDoc} 262 */ 263 public ConfigChangeResult applyConfigurationAdd( 264 VirtualAttributeCfg configuration) 265 { 266 ResultCode resultCode = ResultCode.SUCCESS; 267 boolean adminActionRequired = false; 268 ArrayList<Message> messages = new ArrayList<Message>(); 269 270 configuration.addChangeListener(this); 271 272 if (! configuration.isEnabled()) 273 { 274 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 275 } 276 277 // Make sure that we can parse all of the search filters. 278 LinkedHashSet<SearchFilter> filters = 279 new LinkedHashSet<SearchFilter>(); 280 for (String filterString : configuration.getFilter()) 281 { 282 try 283 { 284 filters.add(SearchFilter.createFilterFromString(filterString)); 285 } 286 catch (DirectoryException de) 287 { 288 if (debugEnabled()) 289 { 290 TRACER.debugCaught(DebugLogLevel.ERROR, de); 291 } 292 293 if (resultCode == ResultCode.SUCCESS) 294 { 295 resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX; 296 } 297 298 Message message = ERR_CONFIG_VATTR_INVALID_SEARCH_FILTER.get( 299 filterString, 300 String.valueOf(configuration.dn()), 301 de.getMessageObject()); 302 messages.add(message); 303 } 304 } 305 306 // Get the name of the class and make sure we can instantiate it as a 307 // certificate mapper. 308 VirtualAttributeProvider<? extends VirtualAttributeCfg> provider = null; 309 if (resultCode == ResultCode.SUCCESS) 310 { 311 String className = configuration.getJavaClass(); 312 try 313 { 314 provider = loadProvider(className, configuration, true); 315 } 316 catch (InitializationException ie) 317 { 318 resultCode = DirectoryServer.getServerErrorResultCode(); 319 messages.add(ie.getMessageObject()); 320 } 321 } 322 323 if (resultCode == ResultCode.SUCCESS) 324 { 325 VirtualAttributeRule rule = 326 new VirtualAttributeRule(configuration.getAttributeType(), provider, 327 configuration.getBaseDN(), 328 configuration.getGroupDN(), 329 filters, 330 configuration.getConflictBehavior()); 331 332 rules.put(configuration.dn(), rule); 333 DirectoryServer.registerVirtualAttribute(rule); 334 } 335 336 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 337 } 338 339 340 341 /** 342 * {@inheritDoc} 343 */ 344 public boolean isConfigurationDeleteAcceptable( 345 VirtualAttributeCfg configuration, 346 List<Message> unacceptableReasons) 347 { 348 // We will always allow getting rid of a virtual attribute rule. 349 return true; 350 } 351 352 353 354 /** 355 * {@inheritDoc} 356 */ 357 public ConfigChangeResult applyConfigurationDelete( 358 VirtualAttributeCfg configuration) 359 { 360 ResultCode resultCode = ResultCode.SUCCESS; 361 boolean adminActionRequired = false; 362 ArrayList<Message> messages = new ArrayList<Message>(); 363 364 VirtualAttributeRule rule = rules.remove(configuration.dn()); 365 if (rule != null) 366 { 367 DirectoryServer.deregisterVirtualAttribute(rule); 368 rule.getProvider().finalizeVirtualAttributeProvider(); 369 } 370 371 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 372 } 373 374 375 376 /** 377 * {@inheritDoc} 378 */ 379 public boolean isConfigurationChangeAcceptable( 380 VirtualAttributeCfg configuration, 381 List<Message> unacceptableReasons) 382 { 383 if (configuration.isEnabled()) 384 { 385 // Get the name of the class and make sure we can instantiate it as a 386 // virtual attribute provider. 387 String className = configuration.getJavaClass(); 388 try 389 { 390 loadProvider(className, configuration, false); 391 } 392 catch (InitializationException ie) 393 { 394 unacceptableReasons.add(ie.getMessageObject()); 395 return false; 396 } 397 } 398 399 // If there were any search filters provided, then make sure they are all 400 // valid. 401 for (String filterString : configuration.getFilter()) 402 { 403 try 404 { 405 SearchFilter.createFilterFromString(filterString); 406 } 407 catch (DirectoryException de) 408 { 409 if (debugEnabled()) 410 { 411 TRACER.debugCaught(DebugLogLevel.ERROR, de); 412 } 413 414 Message message = ERR_CONFIG_VATTR_INVALID_SEARCH_FILTER.get( 415 filterString, 416 String.valueOf(configuration.dn()), 417 de.getMessageObject()); 418 unacceptableReasons.add(message); 419 return false; 420 } 421 } 422 423 // If we've gotten here, then it's fine. 424 return true; 425 } 426 427 428 429 /** 430 * {@inheritDoc} 431 */ 432 public ConfigChangeResult applyConfigurationChange( 433 VirtualAttributeCfg configuration) 434 { 435 ResultCode resultCode = ResultCode.SUCCESS; 436 boolean adminActionRequired = false; 437 ArrayList<Message> messages = new ArrayList<Message>(); 438 439 440 // Get the existing rule if it's already enabled. 441 VirtualAttributeRule existingRule = rules.get(configuration.dn()); 442 443 444 // If the new configuration has the rule disabled, then disable it if it 445 // is enabled, or do nothing if it's already disabled. 446 if (! configuration.isEnabled()) 447 { 448 if (existingRule != null) 449 { 450 DirectoryServer.deregisterVirtualAttribute(existingRule); 451 existingRule.getProvider().finalizeVirtualAttributeProvider(); 452 } 453 454 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 455 } 456 457 458 // Make sure that we can parse all of the search filters. 459 LinkedHashSet<SearchFilter> filters = 460 new LinkedHashSet<SearchFilter>(); 461 for (String filterString : configuration.getFilter()) 462 { 463 try 464 { 465 filters.add(SearchFilter.createFilterFromString(filterString)); 466 } 467 catch (DirectoryException de) 468 { 469 if (debugEnabled()) 470 { 471 TRACER.debugCaught(DebugLogLevel.ERROR, de); 472 } 473 474 if (resultCode == ResultCode.SUCCESS) 475 { 476 resultCode = ResultCode.INVALID_ATTRIBUTE_SYNTAX; 477 } 478 479 Message message = ERR_CONFIG_VATTR_INVALID_SEARCH_FILTER.get( 480 filterString, 481 String.valueOf(configuration.dn()), 482 de.getMessageObject()); 483 messages.add(message); 484 } 485 } 486 487 // Get the name of the class and make sure we can instantiate it as a 488 // certificate mapper. 489 VirtualAttributeProvider<? extends VirtualAttributeCfg> provider = null; 490 if (resultCode == ResultCode.SUCCESS) 491 { 492 String className = configuration.getJavaClass(); 493 try 494 { 495 provider = loadProvider(className, configuration, true); 496 } 497 catch (InitializationException ie) 498 { 499 resultCode = DirectoryServer.getServerErrorResultCode(); 500 messages.add(ie.getMessageObject()); 501 } 502 } 503 504 if (resultCode == ResultCode.SUCCESS) 505 { 506 VirtualAttributeRule rule = 507 new VirtualAttributeRule(configuration.getAttributeType(), provider, 508 configuration.getBaseDN(), 509 configuration.getGroupDN(), 510 filters, 511 configuration.getConflictBehavior()); 512 513 rules.put(configuration.dn(), rule); 514 if (existingRule == null) 515 { 516 DirectoryServer.registerVirtualAttribute(rule); 517 } 518 else 519 { 520 DirectoryServer.replaceVirtualAttribute(existingRule, rule); 521 existingRule.getProvider().finalizeVirtualAttributeProvider(); 522 } 523 } 524 525 return new ConfigChangeResult(resultCode, adminActionRequired, messages); 526 } 527 528 529 530 /** 531 * Loads the specified class, instantiates it as a certificate mapper, and 532 * optionally initializes that instance. 533 * 534 * @param className The fully-qualified name of the certificate mapper 535 * class to load, instantiate, and initialize. 536 * @param configuration The configuration to use to initialize the 537 * virtual attribute provider. It must not be 538 * {@code null}. 539 * @param initialize Indicates whether the virtual attribute provider 540 * instance should be initialized. 541 * 542 * @return The possibly initialized certificate mapper. 543 * 544 * @throws InitializationException If a problem occurred while attempting to 545 * initialize the certificate mapper. 546 */ 547 private VirtualAttributeProvider<? extends VirtualAttributeCfg> 548 loadProvider(String className, VirtualAttributeCfg configuration, 549 boolean initialize) 550 throws InitializationException 551 { 552 try 553 { 554 VirtualAttributeCfgDefn definition = 555 VirtualAttributeCfgDefn.getInstance(); 556 ClassPropertyDefinition propertyDefinition = 557 definition.getJavaClassPropertyDefinition(); 558 Class<? extends VirtualAttributeProvider> providerClass = 559 propertyDefinition.loadClass(className, 560 VirtualAttributeProvider.class); 561 VirtualAttributeProvider<? extends VirtualAttributeCfg> provider = 562 (VirtualAttributeProvider<? extends VirtualAttributeCfg>) 563 providerClass.newInstance(); 564 565 if (initialize) 566 { 567 Method method = provider.getClass().getMethod( 568 "initializeVirtualAttributeProvider", 569 configuration.configurationClass()); 570 method.invoke(provider, configuration); 571 } 572 else 573 { 574 Method method = 575 provider.getClass().getMethod("isConfigurationAcceptable", 576 VirtualAttributeCfg.class, 577 List.class); 578 579 List<Message> unacceptableReasons = new ArrayList<Message>(); 580 Boolean acceptable = (Boolean) method.invoke(provider, configuration, 581 unacceptableReasons); 582 if (! acceptable) 583 { 584 StringBuilder buffer = new StringBuilder(); 585 if (! unacceptableReasons.isEmpty()) 586 { 587 Iterator<Message> iterator = unacceptableReasons.iterator(); 588 buffer.append(iterator.next()); 589 while (iterator.hasNext()) 590 { 591 buffer.append(". "); 592 buffer.append(iterator.next()); 593 } 594 } 595 596 Message message = ERR_CONFIG_VATTR_CONFIG_NOT_ACCEPTABLE.get( 597 String.valueOf(configuration.dn()), buffer.toString()); 598 throw new InitializationException(message); 599 } 600 } 601 602 return provider; 603 } 604 catch (Exception e) 605 { 606 Message message = ERR_CONFIG_VATTR_INITIALIZATION_FAILED. 607 get(className, String.valueOf(configuration.dn()), 608 stackTraceToSingleLineString(e)); 609 throw new InitializationException(message, e); 610 } 611 } 612 } 613