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-2009 Sun Microsystems, Inc. 025 * Portions Copyright 2012-2015 ForgeRock AS. 026 */ 027package org.opends.server.core; 028 029import static org.opends.messages.ConfigMessages.*; 030import static org.opends.messages.CoreMessages.*; 031import static org.opends.server.util.StaticUtils.*; 032 033import java.util.List; 034import java.util.Map; 035import java.util.concurrent.ConcurrentHashMap; 036 037import org.forgerock.i18n.LocalizableMessage; 038import org.forgerock.i18n.slf4j.LocalizedLogger; 039import org.forgerock.opendj.config.server.ConfigException; 040import org.opends.server.admin.AdministrationConnector; 041import org.opends.server.admin.ClassPropertyDefinition; 042import org.opends.server.admin.server.ConfigurationAddListener; 043import org.opends.server.admin.server.ConfigurationChangeListener; 044import org.opends.server.admin.server.ConfigurationDeleteListener; 045import org.opends.server.admin.server.ServerManagementContext; 046import org.opends.server.admin.std.meta.ConnectionHandlerCfgDefn; 047import org.opends.server.admin.std.server.AdministrationConnectorCfg; 048import org.opends.server.admin.std.server.ConnectionHandlerCfg; 049import org.opends.server.admin.std.server.RootCfg; 050import org.opends.server.api.ConnectionHandler; 051import org.opends.server.protocols.ldap.LDAPConnectionHandler; 052import org.forgerock.opendj.config.server.ConfigChangeResult; 053import org.opends.server.types.DN; 054import org.opends.server.types.InitializationException; 055 056/** 057 * This class defines a utility that will be used to manage the 058 * configuration for the set of connection handlers defined in the 059 * Directory Server. It will perform the necessary initialization of 060 * those connection handlers when the server is first started, and 061 * then will manage any changes to them while the server is running. 062 */ 063public class ConnectionHandlerConfigManager implements 064 ConfigurationAddListener<ConnectionHandlerCfg>, 065 ConfigurationDeleteListener<ConnectionHandlerCfg>, 066 ConfigurationChangeListener<ConnectionHandlerCfg> { 067 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 068 069 070 /** 071 * The mapping between configuration entry DNs and their corresponding 072 * connection handler implementations. 073 */ 074 private final Map<DN, ConnectionHandler<?>> connectionHandlers; 075 076 private final ServerContext serverContext; 077 078 /** 079 * Creates a new instance of this connection handler config manager. 080 * 081 * @param serverContext 082 * The server context. 083 */ 084 public ConnectionHandlerConfigManager(ServerContext serverContext) { 085 this.serverContext = serverContext; 086 connectionHandlers = new ConcurrentHashMap<>(); 087 } 088 089 /** {@inheritDoc} */ 090 @Override 091 public ConfigChangeResult applyConfigurationAdd( 092 ConnectionHandlerCfg configuration) { 093 final ConfigChangeResult ccr = new ConfigChangeResult(); 094 095 // Register as a change listener for this connection handler entry 096 // so that we will be notified of any changes that may be made to it. 097 configuration.addChangeListener(this); 098 099 // Ignore this connection handler if it is disabled. 100 if (configuration.isEnabled()) { 101 // The connection handler needs to be enabled. 102 DN dn = configuration.dn(); 103 try { 104 // Attempt to start the connection handler. 105 ConnectionHandler<? extends ConnectionHandlerCfg> connectionHandler = 106 getConnectionHandler(configuration); 107 connectionHandler.start(); 108 109 // Put this connection handler in the hash so that we will be 110 // able to find it if it is altered. 111 connectionHandlers.put(dn, connectionHandler); 112 113 // Register the connection handler with the Directory Server. 114 DirectoryServer.registerConnectionHandler(connectionHandler); 115 } catch (ConfigException e) { 116 logger.traceException(e); 117 118 ccr.addMessage(e.getMessageObject()); 119 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 120 } catch (Exception e) { 121 logger.traceException(e); 122 ccr.addMessage(ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE.get( 123 configuration.getJavaClass(), dn, stackTraceToSingleLineString(e))); 124 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 125 } 126 } 127 128 return ccr; 129 } 130 131 132 133 /** {@inheritDoc} */ 134 @Override 135 public ConfigChangeResult applyConfigurationChange( 136 ConnectionHandlerCfg configuration) { 137 // Attempt to get the existing connection handler. This will only 138 // succeed if it was enabled. 139 DN dn = configuration.dn(); 140 ConnectionHandler<?> connectionHandler = connectionHandlers.get(dn); 141 142 final ConfigChangeResult ccr = new ConfigChangeResult(); 143 144 // See whether the connection handler should be enabled. 145 if (connectionHandler == null) { 146 if (configuration.isEnabled()) { 147 // The connection handler needs to be enabled. 148 try { 149 // Attempt to start the connection handler. 150 connectionHandler = getConnectionHandler(configuration); 151 connectionHandler.start(); 152 153 // Put this connection handler in the hash so that we will 154 // be able to find it if it is altered. 155 connectionHandlers.put(dn, connectionHandler); 156 157 // Register the connection handler with the Directory 158 // Server. 159 DirectoryServer.registerConnectionHandler(connectionHandler); 160 } catch (ConfigException e) { 161 logger.traceException(e); 162 163 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 164 ccr.addMessage(e.getMessageObject()); 165 } catch (Exception e) { 166 logger.traceException(e); 167 168 ccr.addMessage(ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE.get( 169 configuration.getJavaClass(), dn, stackTraceToSingleLineString(e))); 170 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 171 } 172 } 173 } else { 174 if (configuration.isEnabled()) { 175 // The connection handler is currently active, so we don't 176 // need to do anything. Changes to the class name cannot be 177 // applied dynamically, so if the class name did change then 178 // indicate that administrative action is required for that 179 // change to take effect. 180 String className = configuration.getJavaClass(); 181 if (!className.equals(connectionHandler.getClass().getName())) { 182 ccr.setAdminActionRequired(true); 183 } 184 } else { 185 // We need to disable the connection handler. 186 DirectoryServer 187 .deregisterConnectionHandler(connectionHandler); 188 connectionHandlers.remove(dn); 189 190 191 connectionHandler.finalizeConnectionHandler( 192 INFO_CONNHANDLER_CLOSED_BY_DISABLE.get()); 193 } 194 } 195 196 return ccr; 197 } 198 199 200 201 /** {@inheritDoc} */ 202 @Override 203 public ConfigChangeResult applyConfigurationDelete( 204 ConnectionHandlerCfg configuration) { 205 final ConfigChangeResult ccr = new ConfigChangeResult(); 206 207 // See if the entry is registered as a connection handler. If so, 208 // deregister and stop it. We'll try to leave any established 209 // connections alone if possible. 210 DN dn = configuration.dn(); 211 ConnectionHandler<?> connectionHandler = connectionHandlers.get(dn); 212 if (connectionHandler != null) { 213 DirectoryServer.deregisterConnectionHandler(connectionHandler); 214 connectionHandlers.remove(dn); 215 216 connectionHandler.finalizeConnectionHandler( 217 INFO_CONNHANDLER_CLOSED_BY_DELETE.get()); 218 } 219 220 return ccr; 221 } 222 223 224 225 /** 226 * Initializes the configuration associated with the Directory 227 * Server connection handlers. This should only be called at 228 * Directory Server startup. 229 * 230 * @throws ConfigException 231 * If a critical configuration problem prevents the 232 * connection handler initialization from succeeding. 233 * @throws InitializationException 234 * If a problem occurs while initializing the connection 235 * handlers that is not related to the server 236 * configuration. 237 */ 238 public void initializeConnectionHandlerConfig() 239 throws ConfigException, InitializationException { 240 // Clear the set of connection handlers in case of in-core restart. 241 connectionHandlers.clear(); 242 243 // Initialize the admin connector. 244 initializeAdministrationConnectorConfig(); 245 246 // Get the root configuration which acts as the parent of all 247 // connection handlers. 248 ServerManagementContext context = ServerManagementContext 249 .getInstance(); 250 RootCfg root = context.getRootConfiguration(); 251 252 // Register as an add and delete listener so that we can 253 // be notified if new connection handlers are added or existing 254 // connection handlers are removed. 255 root.addConnectionHandlerAddListener(this); 256 root.addConnectionHandlerDeleteListener(this); 257 258 // Initialize existing connection handles. 259 for (String name : root.listConnectionHandlers()) { 260 ConnectionHandlerCfg config = root 261 .getConnectionHandler(name); 262 263 // Register as a change listener for this connection handler 264 // entry so that we will be notified of any changes that may be 265 // made to it. 266 config.addChangeListener(this); 267 268 // Ignore this connection handler if it is disabled. 269 if (config.isEnabled()) { 270 // Note that we don't want to start the connection handler 271 // because we're still in the startup process. Therefore, we 272 // will not do so and allow the server to start it at the very 273 // end of the initialization process. 274 ConnectionHandler<? extends ConnectionHandlerCfg> connectionHandler = 275 getConnectionHandler(config); 276 277 // Put this connection handler in the hash so that we will be 278 // able to find it if it is altered. 279 connectionHandlers.put(config.dn(), connectionHandler); 280 281 // Register the connection handler with the Directory Server. 282 DirectoryServer.registerConnectionHandler(connectionHandler); 283 } 284 } 285 } 286 287 288 289 private void initializeAdministrationConnectorConfig() 290 throws ConfigException, InitializationException { 291 292 RootCfg root = 293 ServerManagementContext.getInstance().getRootConfiguration(); 294 AdministrationConnectorCfg administrationConnectorCfg = 295 root.getAdministrationConnector(); 296 297 AdministrationConnector ac = new AdministrationConnector(serverContext); 298 ac.initializeAdministrationConnector(administrationConnectorCfg); 299 300 // Put this connection handler in the hash so that we will be 301 // able to find it if it is altered. 302 LDAPConnectionHandler connectionHandler = ac.getConnectionHandler(); 303 connectionHandlers.put(administrationConnectorCfg.dn(), connectionHandler); 304 305 // Register the connection handler with the Directory Server. 306 DirectoryServer.registerConnectionHandler(connectionHandler); 307 } 308 309 310 /** {@inheritDoc} */ 311 @Override 312 public boolean isConfigurationAddAcceptable( 313 ConnectionHandlerCfg configuration, 314 List<LocalizableMessage> unacceptableReasons) { 315 return !configuration.isEnabled() 316 || isJavaClassAcceptable(configuration, unacceptableReasons); 317 } 318 319 320 321 /** {@inheritDoc} */ 322 @Override 323 public boolean isConfigurationChangeAcceptable( 324 ConnectionHandlerCfg configuration, 325 List<LocalizableMessage> unacceptableReasons) { 326 return !configuration.isEnabled() 327 || isJavaClassAcceptable(configuration, unacceptableReasons); 328 } 329 330 331 332 /** {@inheritDoc} */ 333 @Override 334 public boolean isConfigurationDeleteAcceptable( 335 ConnectionHandlerCfg configuration, 336 List<LocalizableMessage> unacceptableReasons) { 337 // A delete should always be acceptable, so just return true. 338 return true; 339 } 340 341 342 343 /** Load and initialize the connection handler named in the config. */ 344 private <T extends ConnectionHandlerCfg> ConnectionHandler<T> getConnectionHandler( 345 T config) throws ConfigException 346 { 347 String className = config.getJavaClass(); 348 ConnectionHandlerCfgDefn d = ConnectionHandlerCfgDefn.getInstance(); 349 ClassPropertyDefinition pd = d.getJavaClassPropertyDefinition(); 350 351 try { 352 @SuppressWarnings("rawtypes") 353 Class<? extends ConnectionHandler> theClass = 354 pd.loadClass(className, ConnectionHandler.class); 355 ConnectionHandler<T> connectionHandler = theClass.newInstance(); 356 357 connectionHandler.initializeConnectionHandler(serverContext, config); 358 359 return connectionHandler; 360 } catch (Exception e) { 361 logger.traceException(e); 362 363 LocalizableMessage message = ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE.get( 364 className, config.dn(), stackTraceToSingleLineString(e)); 365 throw new ConfigException(message, e); 366 } 367 } 368 369 370 371 /** 372 * Determines whether or not the new configuration's implementation 373 * class is acceptable. 374 */ 375 private boolean isJavaClassAcceptable( 376 ConnectionHandlerCfg config, 377 List<LocalizableMessage> unacceptableReasons) { 378 String className = config.getJavaClass(); 379 ConnectionHandlerCfgDefn d = ConnectionHandlerCfgDefn.getInstance(); 380 ClassPropertyDefinition pd = d.getJavaClassPropertyDefinition(); 381 382 try { 383 ConnectionHandler<?> connectionHandler = connectionHandlers.get(config.dn()); 384 if (connectionHandler == null) { 385 @SuppressWarnings("rawtypes") 386 Class<? extends ConnectionHandler> theClass = 387 pd.loadClass(className, ConnectionHandler.class); 388 connectionHandler = theClass.newInstance(); 389 } 390 391 return connectionHandler.isConfigurationAcceptable(config, unacceptableReasons); 392 } catch (Exception e) { 393 logger.traceException(e); 394 395 unacceptableReasons.add(ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE.get( 396 className, config.dn(), stackTraceToSingleLineString(e))); 397 return false; 398 } 399 } 400}