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