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