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