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.server.loggers.debug.DebugLogger.*; 033 import org.opends.server.loggers.debug.DebugTracer; 034 import static org.opends.messages.ConfigMessages.*; 035 036 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString; 037 038 import java.lang.reflect.Method; 039 import java.util.ArrayList; 040 import java.util.List; 041 import java.util.concurrent.ConcurrentHashMap; 042 043 import org.opends.server.admin.ClassPropertyDefinition; 044 import org.opends.server.admin.server.ConfigurationAddListener; 045 import org.opends.server.admin.server.ConfigurationChangeListener; 046 import org.opends.server.admin.server.ConfigurationDeleteListener; 047 import org.opends.server.admin.server.ServerManagementContext; 048 import org.opends.server.admin.std.meta.SynchronizationProviderCfgDefn; 049 import org.opends.server.admin.std.server.RootCfg; 050 import org.opends.server.admin.std.server.SynchronizationProviderCfg; 051 import org.opends.server.api.SynchronizationProvider; 052 import org.opends.server.config.ConfigException; 053 import org.opends.server.types.ConfigChangeResult; 054 import org.opends.server.types.DN; 055 import org.opends.server.types.DebugLogLevel; 056 import org.opends.server.types.InitializationException; 057 import org.opends.server.types.ResultCode; 058 059 060 061 /** 062 * This class defines a utility that will be used to manage the configuration 063 * for the set of synchronization providers configured in the Directory Server. 064 * It will perform the necessary initialization of those synchronization 065 * providers when the server is first started, and then will manage any changes 066 * to them while the server is running. 067 */ 068 public class SynchronizationProviderConfigManager 069 implements ConfigurationChangeListener<SynchronizationProviderCfg>, 070 ConfigurationAddListener<SynchronizationProviderCfg>, 071 ConfigurationDeleteListener<SynchronizationProviderCfg> 072 { 073 /** 074 * The tracer object for the debug logger. 075 */ 076 private static final DebugTracer TRACER = getTracer(); 077 078 079 080 081 // The mapping between configuration entry DNs and their corresponding 082 // synchronization provider implementations. 083 private ConcurrentHashMap<DN, 084 SynchronizationProvider<SynchronizationProviderCfg>> registeredProviders = 085 new ConcurrentHashMap<DN, 086 SynchronizationProvider<SynchronizationProviderCfg>>(); 087 088 089 090 091 /** 092 * Creates a new instance of this synchronization provider config manager. 093 */ 094 public SynchronizationProviderConfigManager() 095 { 096 // No implementation is required. 097 } 098 099 100 101 /** 102 * Initializes the configuration associated with the Directory Server 103 * synchronization providers. This should only be called at Directory Server 104 * startup. 105 * 106 * @throws ConfigException If a critical configuration problem prevents any 107 * of the synchronization providers from starting 108 * properly. 109 * 110 * @throws InitializationException If a problem occurs while initializing 111 * any of the synchronization providers that 112 * is not related to the Directory Server 113 * configuration. 114 */ 115 public void initializeSynchronizationProviders() 116 throws ConfigException, InitializationException 117 { 118 // Create an internal server management context and retrieve 119 // the root configuration which has the synchronization provider relation. 120 ServerManagementContext context = ServerManagementContext.getInstance(); 121 RootCfg root = context.getRootConfiguration(); 122 123 // Register as an add and delete listener so that we can 124 // be notified when new synchronization providers are added or existing 125 // sycnhronization providers are removed. 126 root.addSynchronizationProviderAddListener(this); 127 root.addSynchronizationProviderDeleteListener(this); 128 129 // Initialize existing synchronization providers. 130 for (String name : root.listSynchronizationProviders()) 131 { 132 // Get the synchronization provider's configuration. 133 // This will automatically decode and validate its properties. 134 SynchronizationProviderCfg config = root.getSynchronizationProvider(name); 135 136 // Register as a change listener for this synchronization provider 137 // entry so that we can be notified when it is disabled or enabled. 138 config.addChangeListener(this); 139 140 // Ignore this synchronization provider if it is disabled. 141 if (config.isEnabled()) 142 { 143 // Perform initialization, load the synchronization provider's 144 // implementation class and initialize it. 145 SynchronizationProvider<SynchronizationProviderCfg> provider = 146 getSynchronizationProvider(config); 147 148 // Register the synchronization provider with the Directory Server. 149 DirectoryServer.registerSynchronizationProvider(provider); 150 151 // Put this synchronization provider in the hash map so that we will be 152 // able to find it if it is deleted or disabled. 153 registeredProviders.put(config.dn(), provider); 154 } 155 } 156 } 157 158 159 160 /** 161 * {@inheritDoc} 162 */ 163 public ConfigChangeResult applyConfigurationChange( 164 SynchronizationProviderCfg configuration) 165 { 166 // Default result code. 167 ResultCode resultCode = ResultCode.SUCCESS; 168 boolean adminActionRequired = false; 169 ArrayList<Message> messages = new ArrayList<Message>(); 170 171 // Attempt to get the existing synchronization provider. This will only 172 // succeed if it is currently enabled. 173 DN dn = configuration.dn(); 174 SynchronizationProvider<SynchronizationProviderCfg> provider = 175 registeredProviders.get(dn); 176 177 // See whether the synchronization provider should be enabled. 178 if (provider == null) 179 { 180 if (configuration.isEnabled()) 181 { 182 // The synchronization provider needs to be enabled. Load, initialize, 183 // and register the synchronization provider as per the add listener 184 // method. 185 try 186 { 187 // Perform initialization, load the synchronization provider's 188 // implementation class and initialize it. 189 provider = getSynchronizationProvider(configuration); 190 191 // Register the synchronization provider with the Directory Server. 192 DirectoryServer.registerSynchronizationProvider(provider); 193 194 // Put this synchronization provider in the hash map so that we will 195 // be able to find it if it is deleted or disabled. 196 registeredProviders.put(configuration.dn(), provider); 197 } 198 catch (ConfigException e) 199 { 200 if (debugEnabled()) 201 { 202 TRACER.debugCaught(DebugLogLevel.ERROR, e); 203 messages.add(e.getMessageObject()); 204 resultCode = DirectoryServer.getServerErrorResultCode(); 205 } 206 } 207 catch (Exception e) 208 { 209 if (debugEnabled()) 210 { 211 TRACER.debugCaught(DebugLogLevel.ERROR, e); 212 } 213 214 messages.add(ERR_CONFIG_SYNCH_ERROR_INITIALIZING_PROVIDER.get( 215 String.valueOf(configuration.getJavaClass()), 216 String.valueOf(configuration.dn()))); 217 resultCode = DirectoryServer.getServerErrorResultCode(); 218 } 219 } 220 } 221 else 222 { 223 if (configuration.isEnabled()) 224 { 225 // The synchronization provider is currently active, so we don't 226 // need to do anything. Changes to the class name cannot be 227 // applied dynamically, so if the class name did change then 228 // indicate that administrative action is required for that 229 // change to take effect. 230 String className = configuration.getJavaClass(); 231 if (!className.equals(provider.getClass().getName())) 232 { 233 adminActionRequired = true; 234 } 235 } 236 else 237 { 238 // The connection handler is being disabled so remove it from 239 // the DirectorySerevr list, shut it down and remove it from the 240 // hash map. 241 DirectoryServer.deregisterSynchronizationProvider(provider); 242 provider.finalizeSynchronizationProvider(); 243 registeredProviders.remove(dn); 244 } 245 } 246 // Return the configuration result. 247 return new ConfigChangeResult(resultCode, adminActionRequired, 248 messages); 249 } 250 251 252 253 /** 254 * {@inheritDoc} 255 */ 256 public boolean isConfigurationChangeAcceptable( 257 SynchronizationProviderCfg configuration, 258 List<Message> unacceptableReasons) 259 { 260 if (configuration.isEnabled()) 261 { 262 // It's enabled so always validate the class. 263 return isJavaClassAcceptable(configuration, unacceptableReasons); 264 } else 265 { 266 // It's disabled so ignore it. 267 return true; 268 } 269 } 270 271 272 273 /** 274 * {@inheritDoc} 275 */ 276 public ConfigChangeResult applyConfigurationAdd( 277 SynchronizationProviderCfg configuration) 278 { 279 // Default result code. 280 ResultCode resultCode = ResultCode.SUCCESS; 281 boolean adminActionRequired = false; 282 ArrayList<Message> messages = new ArrayList<Message>(); 283 284 // Register as a change listener for this synchronization provider entry 285 // so that we will be notified if when it is disabled or enabled. 286 configuration.addChangeListener(this); 287 288 // Ignore this synchronization provider if it is disabled. 289 if (configuration.isEnabled()) 290 { 291 try 292 { 293 // Perform initialization, load the synchronization provider's 294 // implementation class and initialize it. 295 SynchronizationProvider<SynchronizationProviderCfg> provider = 296 getSynchronizationProvider(configuration); 297 298 // Register the synchronization provider with the Directory Server. 299 DirectoryServer.registerSynchronizationProvider(provider); 300 301 // Put this synchronization provider in the hash map so that we will be 302 // able to find it if it is deleted or disabled. 303 registeredProviders.put(configuration.dn(), provider); 304 } 305 catch (ConfigException e) 306 { 307 if (debugEnabled()) 308 { 309 TRACER.debugCaught(DebugLogLevel.ERROR, e); 310 messages.add(e.getMessageObject()); 311 resultCode = DirectoryServer.getServerErrorResultCode(); 312 } 313 } 314 catch (Exception e) 315 { 316 if (debugEnabled()) 317 { 318 TRACER.debugCaught(DebugLogLevel.ERROR, e); 319 } 320 321 messages.add(ERR_CONFIG_SYNCH_ERROR_INITIALIZING_PROVIDER.get( 322 String.valueOf(configuration.getJavaClass()), 323 String.valueOf(configuration.dn()))); 324 resultCode = DirectoryServer.getServerErrorResultCode(); 325 } 326 } 327 328 // Return the configuration result. 329 return new ConfigChangeResult(resultCode, adminActionRequired, 330 messages); 331 } 332 333 334 335 /** 336 * {@inheritDoc} 337 */ 338 public boolean isConfigurationAddAcceptable( 339 SynchronizationProviderCfg configuration, 340 List<Message> unacceptableReasons) 341 { 342 if (configuration.isEnabled()) 343 { 344 // It's enabled so always validate the class. 345 return isJavaClassAcceptable(configuration, unacceptableReasons); 346 } else 347 { 348 // It's disabled so ignore it. 349 return true; 350 } 351 } 352 353 354 355 /** 356 * Check if the class provided in the configuration is an acceptable 357 * java class for a synchronization provider. 358 * 359 * @param configuration The configuration for which the class must be 360 * checked. 361 * @return true if the class is acceptable or false if not. 362 */ 363 @SuppressWarnings("unchecked") 364 private SynchronizationProvider<SynchronizationProviderCfg> 365 getSynchronizationProvider(SynchronizationProviderCfg configuration) 366 throws ConfigException 367 { 368 String className = configuration.getJavaClass(); 369 SynchronizationProviderCfgDefn d = 370 SynchronizationProviderCfgDefn.getInstance(); 371 ClassPropertyDefinition pd = 372 d.getJavaClassPropertyDefinition(); 373 374 // Load the class 375 Class<? extends SynchronizationProvider> theClass; 376 SynchronizationProvider<SynchronizationProviderCfg> provider; 377 try 378 { 379 theClass = pd.loadClass(className, SynchronizationProvider.class); 380 } catch (Exception e) 381 { 382 // Handle the exception: put a message in the unacceptable reasons. 383 Message message = ERR_CONFIG_SYNCH_UNABLE_TO_LOAD_PROVIDER_CLASS. 384 get(String.valueOf(className), String.valueOf(configuration.dn()), 385 stackTraceToSingleLineString(e)); 386 throw new ConfigException(message, e); 387 } 388 try 389 { 390 // Instantiate the class. 391 provider = theClass.newInstance(); 392 } catch (Exception e) 393 { 394 // Handle the exception: put a message in the unacceptable reasons. 395 Message message = ERR_CONFIG_SYNCH_UNABLE_TO_INSTANTIATE_PROVIDER. 396 get(String.valueOf(className), String.valueOf(configuration.dn()), 397 stackTraceToSingleLineString(e)); 398 throw new ConfigException(message, e); 399 } 400 try 401 { 402 // Initialize the Synchronization Provider. 403 provider.initializeSynchronizationProvider(configuration); 404 } catch (Exception e) 405 { 406 // Handle the exception: put a message in the unacceptable reasons. 407 Message message = ERR_CONFIG_SYNCH_ERROR_INITIALIZING_PROVIDER.get( 408 String.valueOf(className), String.valueOf(configuration.dn())); 409 throw new ConfigException(message, e); 410 } 411 return provider; 412 } 413 414 /** 415 * Check if the class provided in the configuration is an acceptable 416 * java class for a synchronization provider. 417 * 418 * @param configuration The configuration for which the class must be 419 * checked. 420 * @param unacceptableReasons A list containing the reasons why the class is 421 * not acceptable. 422 * 423 * @return true if the class is acceptable or false if not. 424 */ 425 private boolean isJavaClassAcceptable( 426 SynchronizationProviderCfg configuration, 427 List<Message> unacceptableReasons) 428 { 429 String className = configuration.getJavaClass(); 430 SynchronizationProviderCfgDefn d = 431 SynchronizationProviderCfgDefn.getInstance(); 432 ClassPropertyDefinition pd = 433 d.getJavaClassPropertyDefinition(); 434 435 // Load the class and cast it to a synchronizationProvider. 436 SynchronizationProvider provider = null; 437 Class<? extends SynchronizationProvider> theClass; 438 try 439 { 440 theClass = pd.loadClass(className, SynchronizationProvider.class); 441 provider = theClass.newInstance(); 442 } catch (Exception e) 443 { 444 // Handle the exception: put a message in the unacceptable reasons. 445 Message message = ERR_CONFIG_SYNCH_UNABLE_TO_LOAD_PROVIDER_CLASS.get( 446 String.valueOf(className), 447 String.valueOf(configuration.dn()), 448 stackTraceToSingleLineString(e)); 449 unacceptableReasons.add(message); 450 return false; 451 } 452 // Check that the implementation class implements the correct interface. 453 try 454 { 455 // Determine the initialization method to use: it must take a 456 // single parameter which is the exact type of the configuration 457 // object. 458 Method method = theClass.getMethod("isConfigurationAcceptable", 459 SynchronizationProviderCfg.class, 460 List.class); 461 Boolean acceptable = (Boolean) method.invoke(provider, configuration, 462 unacceptableReasons); 463 464 if (! acceptable) 465 { 466 return false; 467 } 468 } catch (Exception e) 469 { 470 // Handle the exception: put a message in the unacceptable reasons. 471 Message message = ERR_CONFIG_SYNCH_UNABLE_TO_INSTANTIATE_PROVIDER.get( 472 String.valueOf(className), 473 String.valueOf(configuration.dn()), 474 stackTraceToSingleLineString(e)); 475 unacceptableReasons.add(message); 476 return false; 477 } 478 479 // The class is valid as far as we can tell. 480 return true; 481 } 482 483 /** 484 * {@inheritDoc} 485 */ 486 public ConfigChangeResult applyConfigurationDelete( 487 SynchronizationProviderCfg configuration) 488 { 489 // Default result code. 490 ResultCode resultCode = ResultCode.SUCCESS; 491 boolean adminActionRequired = false; 492 493 // See if the entry is registered as a synchronization provider. If so, 494 // deregister and stop it. 495 DN dn = configuration.dn(); 496 SynchronizationProvider provider = registeredProviders.get(dn); 497 if (provider != null) 498 { 499 DirectoryServer.deregisterSynchronizationProvider(provider); 500 provider.finalizeSynchronizationProvider(); 501 } 502 return new ConfigChangeResult(resultCode, adminActionRequired); 503 } 504 505 506 507 /** 508 * {@inheritDoc} 509 */ 510 public boolean isConfigurationDeleteAcceptable( 511 SynchronizationProviderCfg configuration, 512 List<Message> unacceptableReasons) 513 { 514 // A delete should always be acceptable, so just return true. 515 return true; 516 } 517 } 518 519 520 521