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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2008 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027package org.opends.server.core; 028 029import static org.opends.messages.ConfigMessages.*; 030import static org.opends.server.util.StaticUtils.*; 031 032import java.util.List; 033import java.util.concurrent.ConcurrentHashMap; 034 035import org.forgerock.i18n.LocalizableMessage; 036import org.forgerock.i18n.slf4j.LocalizedLogger; 037import org.forgerock.opendj.config.server.ConfigException; 038import org.opends.server.admin.ClassPropertyDefinition; 039import org.opends.server.admin.server.ConfigurationAddListener; 040import org.opends.server.admin.server.ConfigurationChangeListener; 041import org.opends.server.admin.server.ConfigurationDeleteListener; 042import org.opends.server.admin.server.ServerManagementContext; 043import org.opends.server.admin.std.meta.SynchronizationProviderCfgDefn; 044import org.opends.server.admin.std.server.RootCfg; 045import org.opends.server.admin.std.server.SynchronizationProviderCfg; 046import org.opends.server.api.SynchronizationProvider; 047import org.forgerock.opendj.config.server.ConfigChangeResult; 048import org.opends.server.types.DN; 049import org.opends.server.types.InitializationException; 050 051/** 052 * This class defines a utility that will be used to manage the configuration 053 * for the set of synchronization providers configured in the Directory Server. 054 * It will perform the necessary initialization of those synchronization 055 * providers when the server is first started, and then will manage any changes 056 * to them while the server is running. 057 */ 058public class SynchronizationProviderConfigManager 059 implements ConfigurationChangeListener<SynchronizationProviderCfg>, 060 ConfigurationAddListener<SynchronizationProviderCfg>, 061 ConfigurationDeleteListener<SynchronizationProviderCfg> 062{ 063 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 064 065 /** 066 * The mapping between configuration entry DNs and their corresponding 067 * synchronization provider implementations. 068 */ 069 private final ConcurrentHashMap<DN,SynchronizationProvider<SynchronizationProviderCfg>> registeredProviders; 070 071 private final ServerContext serverContext; 072 073 /** 074 * Creates a new instance of this synchronization provider config manager. 075 * 076 * @param serverContext 077 * The server context. 078 */ 079 public SynchronizationProviderConfigManager(ServerContext serverContext) 080 { 081 this.serverContext = serverContext; 082 registeredProviders = new ConcurrentHashMap<>(); 083 } 084 085 /** 086 * Initializes the configuration associated with the Directory Server 087 * synchronization providers. This should only be called at Directory Server 088 * startup. 089 * 090 * @throws ConfigException If a critical configuration problem prevents any 091 * of the synchronization providers from starting 092 * properly. 093 * 094 * @throws InitializationException If a problem occurs while initializing 095 * any of the synchronization providers that 096 * is not related to the Directory Server 097 * configuration. 098 */ 099 public void initializeSynchronizationProviders() 100 throws ConfigException, InitializationException 101 { 102 // Create an internal server management context and retrieve 103 // the root configuration which has the synchronization provider relation. 104 ServerManagementContext context = ServerManagementContext.getInstance(); 105 RootCfg root = context.getRootConfiguration(); 106 107 // Register as an add and delete listener so that we can 108 // be notified when new synchronization providers are added or existing 109 // synchronization providers are removed. 110 root.addSynchronizationProviderAddListener(this); 111 root.addSynchronizationProviderDeleteListener(this); 112 113 // Initialize existing synchronization providers. 114 for (String name : root.listSynchronizationProviders()) 115 { 116 // Get the synchronization provider's configuration. 117 // This will automatically decode and validate its properties. 118 SynchronizationProviderCfg config = root.getSynchronizationProvider(name); 119 120 // Register as a change listener for this synchronization provider 121 // entry so that we can be notified when it is disabled or enabled. 122 config.addChangeListener(this); 123 124 // Ignore this synchronization provider if it is disabled. 125 if (config.isEnabled()) 126 { 127 // Perform initialization, load the synchronization provider's 128 // implementation class and initialize it. 129 SynchronizationProvider<SynchronizationProviderCfg> provider = 130 getSynchronizationProvider(config); 131 132 // Register the synchronization provider with the Directory Server. 133 DirectoryServer.registerSynchronizationProvider(provider); 134 135 // Put this synchronization provider in the hash map so that we will be 136 // able to find it if it is deleted or disabled. 137 registeredProviders.put(config.dn(), provider); 138 } 139 } 140 } 141 142 143 144 /** {@inheritDoc} */ 145 @Override 146 public ConfigChangeResult applyConfigurationChange( 147 SynchronizationProviderCfg configuration) 148 { 149 final ConfigChangeResult ccr = new ConfigChangeResult(); 150 151 // Attempt to get the existing synchronization provider. This will only 152 // succeed if it is currently enabled. 153 DN dn = configuration.dn(); 154 SynchronizationProvider<SynchronizationProviderCfg> provider = 155 registeredProviders.get(dn); 156 157 // See whether the synchronization provider should be enabled. 158 if (provider == null) 159 { 160 if (configuration.isEnabled()) 161 { 162 // The synchronization provider needs to be enabled. Load, initialize, 163 // and register the synchronization provider as per the add listener 164 // method. 165 try 166 { 167 // Perform initialization, load the synchronization provider's 168 // implementation class and initialize it. 169 provider = getSynchronizationProvider(configuration); 170 171 // Register the synchronization provider with the Directory Server. 172 DirectoryServer.registerSynchronizationProvider(provider); 173 174 // Put this synchronization provider in the hash map so that we will 175 // be able to find it if it is deleted or disabled. 176 registeredProviders.put(configuration.dn(), provider); 177 } 178 catch (ConfigException e) 179 { 180 if (logger.isTraceEnabled()) 181 { 182 logger.traceException(e); 183 ccr.addMessage(e.getMessageObject()); 184 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 185 } 186 } 187 catch (Exception e) 188 { 189 logger.traceException(e); 190 191 ccr.addMessage(ERR_CONFIG_SYNCH_ERROR_INITIALIZING_PROVIDER.get(configuration.dn(), 192 stackTraceToSingleLineString(e))); 193 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 194 } 195 } 196 } 197 else 198 { 199 if (configuration.isEnabled()) 200 { 201 // The synchronization provider is currently active, so we don't 202 // need to do anything. Changes to the class name cannot be 203 // applied dynamically, so if the class name did change then 204 // indicate that administrative action is required for that 205 // change to take effect. 206 String className = configuration.getJavaClass(); 207 if (!className.equals(provider.getClass().getName())) 208 { 209 ccr.setAdminActionRequired(true); 210 } 211 } 212 else 213 { 214 // The connection handler is being disabled so remove it from 215 // the DirectorySerevr list, shut it down and remove it from the 216 // hash map. 217 DirectoryServer.deregisterSynchronizationProvider(provider); 218 provider.finalizeSynchronizationProvider(); 219 registeredProviders.remove(dn); 220 } 221 } 222 return ccr; 223 } 224 225 226 227 /** {@inheritDoc} */ 228 @Override 229 public boolean isConfigurationChangeAcceptable( 230 SynchronizationProviderCfg configuration, 231 List<LocalizableMessage> unacceptableReasons) 232 { 233 return !configuration.isEnabled() 234 || isJavaClassAcceptable(configuration, unacceptableReasons); 235 } 236 237 238 239 /** {@inheritDoc} */ 240 @Override 241 public ConfigChangeResult applyConfigurationAdd( 242 SynchronizationProviderCfg configuration) 243 { 244 final ConfigChangeResult ccr = new ConfigChangeResult(); 245 246 // Register as a change listener for this synchronization provider entry 247 // so that we will be notified if when it is disabled or enabled. 248 configuration.addChangeListener(this); 249 250 // Ignore this synchronization provider if it is disabled. 251 if (configuration.isEnabled()) 252 { 253 try 254 { 255 // Perform initialization, load the synchronization provider's 256 // implementation class and initialize it. 257 SynchronizationProvider<SynchronizationProviderCfg> provider = 258 getSynchronizationProvider(configuration); 259 260 // Register the synchronization provider with the Directory Server. 261 DirectoryServer.registerSynchronizationProvider(provider); 262 263 // Put this synchronization provider in the hash map so that we will be 264 // able to find it if it is deleted or disabled. 265 registeredProviders.put(configuration.dn(), provider); 266 } 267 catch (ConfigException e) 268 { 269 if (logger.isTraceEnabled()) 270 { 271 logger.traceException(e); 272 ccr.addMessage(e.getMessageObject()); 273 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 274 } 275 } 276 catch (Exception e) 277 { 278 logger.traceException(e); 279 280 ccr.addMessage(ERR_CONFIG_SYNCH_ERROR_INITIALIZING_PROVIDER.get(configuration.dn(), 281 stackTraceToSingleLineString(e))); 282 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 283 } 284 } 285 286 return ccr; 287 } 288 289 290 291 /** {@inheritDoc} */ 292 @Override 293 public boolean isConfigurationAddAcceptable( 294 SynchronizationProviderCfg configuration, 295 List<LocalizableMessage> unacceptableReasons) 296 { 297 return !configuration.isEnabled() 298 || isJavaClassAcceptable(configuration, unacceptableReasons); 299 } 300 301 302 303 /** 304 * Check if the class provided in the configuration is an acceptable 305 * java class for a synchronization provider. 306 * 307 * @param configuration The configuration for which the class must be 308 * checked. 309 * @return true if the class is acceptable or false if not. 310 */ 311 @SuppressWarnings("unchecked") 312 private SynchronizationProvider<SynchronizationProviderCfg> 313 getSynchronizationProvider(SynchronizationProviderCfg configuration) 314 throws ConfigException 315 { 316 String className = configuration.getJavaClass(); 317 SynchronizationProviderCfgDefn d = 318 SynchronizationProviderCfgDefn.getInstance(); 319 ClassPropertyDefinition pd = 320 d.getJavaClassPropertyDefinition(); 321 322 // Load the class 323 Class<? extends SynchronizationProvider> theClass; 324 SynchronizationProvider<SynchronizationProviderCfg> provider; 325 try 326 { 327 theClass = pd.loadClass(className, SynchronizationProvider.class); 328 } catch (Exception e) 329 { 330 // Handle the exception: put a message in the unacceptable reasons. 331 LocalizableMessage message = ERR_CONFIG_SYNCH_UNABLE_TO_LOAD_PROVIDER_CLASS. 332 get(className, configuration.dn(), stackTraceToSingleLineString(e)); 333 throw new ConfigException(message, e); 334 } 335 try 336 { 337 // Instantiate the class. 338 provider = theClass.newInstance(); 339 } catch (Exception e) 340 { 341 // Handle the exception: put a message in the unacceptable reasons. 342 LocalizableMessage message = ERR_CONFIG_SYNCH_UNABLE_TO_INSTANTIATE_PROVIDER. 343 get(className, configuration.dn(), stackTraceToSingleLineString(e)); 344 throw new ConfigException(message, e); 345 } 346 try 347 { 348 // Initialize the Synchronization Provider. 349 provider.initializeSynchronizationProvider(configuration); 350 } catch (Exception e) 351 { 352 try 353 { 354 provider.finalizeSynchronizationProvider(); 355 } 356 catch(Exception ce) 357 {} 358 359 // Handle the exception: put a message in the unacceptable reasons. 360 throw new ConfigException( 361 ERR_CONFIG_SYNCH_ERROR_INITIALIZING_PROVIDER.get(configuration.dn(), stackTraceToSingleLineString(e)), e); 362 } 363 return provider; 364 } 365 366 /** 367 * Check if the class provided in the configuration is an acceptable 368 * java class for a synchronization provider. 369 * 370 * @param configuration The configuration for which the class must be 371 * checked. 372 * @param unacceptableReasons A list containing the reasons why the class is 373 * not acceptable. 374 * 375 * @return true if the class is acceptable or false if not. 376 */ 377 private boolean isJavaClassAcceptable( 378 SynchronizationProviderCfg configuration, 379 List<LocalizableMessage> unacceptableReasons) 380 { 381 String className = configuration.getJavaClass(); 382 SynchronizationProviderCfgDefn d = 383 SynchronizationProviderCfgDefn.getInstance(); 384 ClassPropertyDefinition pd = d.getJavaClassPropertyDefinition(); 385 386 try 387 { 388 Class<? extends SynchronizationProvider> theClass = 389 pd.loadClass(className, SynchronizationProvider.class); 390 SynchronizationProvider provider = theClass.newInstance(); 391 392 return provider.isConfigurationAcceptable(configuration, 393 unacceptableReasons); 394 } catch (Exception e) 395 { 396 // Handle the exception: put a message in the unacceptable reasons. 397 LocalizableMessage message = ERR_CONFIG_SYNCH_UNABLE_TO_INSTANTIATE_PROVIDER.get( 398 className, configuration.dn(), stackTraceToSingleLineString(e)); 399 unacceptableReasons.add(message); 400 return false; 401 } 402 } 403 404 /** {@inheritDoc} */ 405 @Override 406 public ConfigChangeResult applyConfigurationDelete( 407 SynchronizationProviderCfg configuration) 408 { 409 final ConfigChangeResult ccr = new ConfigChangeResult(); 410 411 // See if the entry is registered as a synchronization provider. If so, 412 // deregister and stop it. 413 DN dn = configuration.dn(); 414 SynchronizationProvider provider = registeredProviders.get(dn); 415 if (provider != null) 416 { 417 DirectoryServer.deregisterSynchronizationProvider(provider); 418 provider.finalizeSynchronizationProvider(); 419 } 420 return ccr; 421 } 422 423 424 425 /** {@inheritDoc} */ 426 @Override 427 public boolean isConfigurationDeleteAcceptable( 428 SynchronizationProviderCfg configuration, 429 List<LocalizableMessage> unacceptableReasons) 430 { 431 // A delete should always be acceptable, so just return true. 432 return true; 433 } 434}