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