/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt * or http://forgerock.org/license/CDDLv1.0.html. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at legal-notices/CDDLv1_0.txt. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Copyright 2006-2009 Sun Microsystems, Inc. * Portions Copyright 2012-2015 ForgeRock AS. */ package org.opends.server.core; import static org.opends.messages.ConfigMessages.*; import static org.opends.messages.CoreMessages.*; import static org.opends.server.util.StaticUtils.*; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.forgerock.i18n.LocalizableMessage; import org.forgerock.i18n.slf4j.LocalizedLogger; import org.forgerock.opendj.config.server.ConfigException; import org.opends.server.admin.AdministrationConnector; import org.opends.server.admin.ClassPropertyDefinition; import org.opends.server.admin.server.ConfigurationAddListener; import org.opends.server.admin.server.ConfigurationChangeListener; import org.opends.server.admin.server.ConfigurationDeleteListener; import org.opends.server.admin.server.ServerManagementContext; import org.opends.server.admin.std.meta.ConnectionHandlerCfgDefn; import org.opends.server.admin.std.server.AdministrationConnectorCfg; import org.opends.server.admin.std.server.ConnectionHandlerCfg; import org.opends.server.admin.std.server.RootCfg; import org.opends.server.api.ConnectionHandler; import org.opends.server.protocols.ldap.LDAPConnectionHandler; import org.forgerock.opendj.config.server.ConfigChangeResult; import org.opends.server.types.DN; import org.opends.server.types.InitializationException; /** * This class defines a utility that will be used to manage the * configuration for the set of connection handlers defined in the * Directory Server. It will perform the necessary initialization of * those connection handlers when the server is first started, and * then will manage any changes to them while the server is running. */ public class ConnectionHandlerConfigManager implements ConfigurationAddListener<ConnectionHandlerCfg>, ConfigurationDeleteListener<ConnectionHandlerCfg>, ConfigurationChangeListener<ConnectionHandlerCfg> { private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); /** * The mapping between configuration entry DNs and their corresponding * connection handler implementations. */ private final Map<DN, ConnectionHandler<?>> connectionHandlers; private final ServerContext serverContext; /** * Creates a new instance of this connection handler config manager. * * @param serverContext * The server context. */ public ConnectionHandlerConfigManager(ServerContext serverContext) { this.serverContext = serverContext; connectionHandlers = new ConcurrentHashMap<>(); } /** {@inheritDoc} */ @Override public ConfigChangeResult applyConfigurationAdd( ConnectionHandlerCfg configuration) { final ConfigChangeResult ccr = new ConfigChangeResult(); // Register as a change listener for this connection handler entry // so that we will be notified of any changes that may be made to it. configuration.addChangeListener(this); // Ignore this connection handler if it is disabled. if (configuration.isEnabled()) { // The connection handler needs to be enabled. DN dn = configuration.dn(); try { // Attempt to start the connection handler. ConnectionHandler<? extends ConnectionHandlerCfg> connectionHandler = getConnectionHandler(configuration); connectionHandler.start(); // Put this connection handler in the hash so that we will be // able to find it if it is altered. connectionHandlers.put(dn, connectionHandler); // Register the connection handler with the Directory Server. DirectoryServer.registerConnectionHandler(connectionHandler); } catch (ConfigException e) { logger.traceException(e); ccr.addMessage(e.getMessageObject()); ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); } catch (Exception e) { logger.traceException(e); ccr.addMessage(ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE.get( configuration.getJavaClass(), dn, stackTraceToSingleLineString(e))); ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); } } return ccr; } /** {@inheritDoc} */ @Override public ConfigChangeResult applyConfigurationChange( ConnectionHandlerCfg configuration) { // Attempt to get the existing connection handler. This will only // succeed if it was enabled. DN dn = configuration.dn(); ConnectionHandler<?> connectionHandler = connectionHandlers.get(dn); final ConfigChangeResult ccr = new ConfigChangeResult(); // See whether the connection handler should be enabled. if (connectionHandler == null) { if (configuration.isEnabled()) { // The connection handler needs to be enabled. try { // Attempt to start the connection handler. connectionHandler = getConnectionHandler(configuration); connectionHandler.start(); // Put this connection handler in the hash so that we will // be able to find it if it is altered. connectionHandlers.put(dn, connectionHandler); // Register the connection handler with the Directory // Server. DirectoryServer.registerConnectionHandler(connectionHandler); } catch (ConfigException e) { logger.traceException(e); ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); ccr.addMessage(e.getMessageObject()); } catch (Exception e) { logger.traceException(e); ccr.addMessage(ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE.get( configuration.getJavaClass(), dn, stackTraceToSingleLineString(e))); ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); } } } else { if (configuration.isEnabled()) { // The connection handler is currently active, so we don't // need to do anything. Changes to the class name cannot be // applied dynamically, so if the class name did change then // indicate that administrative action is required for that // change to take effect. String className = configuration.getJavaClass(); if (!className.equals(connectionHandler.getClass().getName())) { ccr.setAdminActionRequired(true); } } else { // We need to disable the connection handler. DirectoryServer .deregisterConnectionHandler(connectionHandler); connectionHandlers.remove(dn); connectionHandler.finalizeConnectionHandler( INFO_CONNHANDLER_CLOSED_BY_DISABLE.get()); } } return ccr; } /** {@inheritDoc} */ @Override public ConfigChangeResult applyConfigurationDelete( ConnectionHandlerCfg configuration) { final ConfigChangeResult ccr = new ConfigChangeResult(); // See if the entry is registered as a connection handler. If so, // deregister and stop it. We'll try to leave any established // connections alone if possible. DN dn = configuration.dn(); ConnectionHandler<?> connectionHandler = connectionHandlers.get(dn); if (connectionHandler != null) { DirectoryServer.deregisterConnectionHandler(connectionHandler); connectionHandlers.remove(dn); connectionHandler.finalizeConnectionHandler( INFO_CONNHANDLER_CLOSED_BY_DELETE.get()); } return ccr; } /** * Initializes the configuration associated with the Directory * Server connection handlers. This should only be called at * Directory Server startup. * * @throws ConfigException * If a critical configuration problem prevents the * connection handler initialization from succeeding. * @throws InitializationException * If a problem occurs while initializing the connection * handlers that is not related to the server * configuration. */ public void initializeConnectionHandlerConfig() throws ConfigException, InitializationException { // Clear the set of connection handlers in case of in-core restart. connectionHandlers.clear(); // Initialize the admin connector. initializeAdministrationConnectorConfig(); // Get the root configuration which acts as the parent of all // connection handlers. ServerManagementContext context = ServerManagementContext .getInstance(); RootCfg root = context.getRootConfiguration(); // Register as an add and delete listener so that we can // be notified if new connection handlers are added or existing // connection handlers are removed. root.addConnectionHandlerAddListener(this); root.addConnectionHandlerDeleteListener(this); // Initialize existing connection handles. for (String name : root.listConnectionHandlers()) { ConnectionHandlerCfg config = root .getConnectionHandler(name); // Register as a change listener for this connection handler // entry so that we will be notified of any changes that may be // made to it. config.addChangeListener(this); // Ignore this connection handler if it is disabled. if (config.isEnabled()) { // Note that we don't want to start the connection handler // because we're still in the startup process. Therefore, we // will not do so and allow the server to start it at the very // end of the initialization process. ConnectionHandler<? extends ConnectionHandlerCfg> connectionHandler = getConnectionHandler(config); // Put this connection handler in the hash so that we will be // able to find it if it is altered. connectionHandlers.put(config.dn(), connectionHandler); // Register the connection handler with the Directory Server. DirectoryServer.registerConnectionHandler(connectionHandler); } } } private void initializeAdministrationConnectorConfig() throws ConfigException, InitializationException { RootCfg root = ServerManagementContext.getInstance().getRootConfiguration(); AdministrationConnectorCfg administrationConnectorCfg = root.getAdministrationConnector(); AdministrationConnector ac = new AdministrationConnector(serverContext); ac.initializeAdministrationConnector(administrationConnectorCfg); // Put this connection handler in the hash so that we will be // able to find it if it is altered. LDAPConnectionHandler connectionHandler = ac.getConnectionHandler(); connectionHandlers.put(administrationConnectorCfg.dn(), connectionHandler); // Register the connection handler with the Directory Server. DirectoryServer.registerConnectionHandler(connectionHandler); } /** {@inheritDoc} */ @Override public boolean isConfigurationAddAcceptable( ConnectionHandlerCfg configuration, List<LocalizableMessage> unacceptableReasons) { return !configuration.isEnabled() || isJavaClassAcceptable(configuration, unacceptableReasons); } /** {@inheritDoc} */ @Override public boolean isConfigurationChangeAcceptable( ConnectionHandlerCfg configuration, List<LocalizableMessage> unacceptableReasons) { return !configuration.isEnabled() || isJavaClassAcceptable(configuration, unacceptableReasons); } /** {@inheritDoc} */ @Override public boolean isConfigurationDeleteAcceptable( ConnectionHandlerCfg configuration, List<LocalizableMessage> unacceptableReasons) { // A delete should always be acceptable, so just return true. return true; } /** Load and initialize the connection handler named in the config. */ private <T extends ConnectionHandlerCfg> ConnectionHandler<T> getConnectionHandler( T config) throws ConfigException { String className = config.getJavaClass(); ConnectionHandlerCfgDefn d = ConnectionHandlerCfgDefn.getInstance(); ClassPropertyDefinition pd = d.getJavaClassPropertyDefinition(); try { @SuppressWarnings("rawtypes") Class<? extends ConnectionHandler> theClass = pd.loadClass(className, ConnectionHandler.class); ConnectionHandler<T> connectionHandler = theClass.newInstance(); connectionHandler.initializeConnectionHandler(serverContext, config); return connectionHandler; } catch (Exception e) { logger.traceException(e); LocalizableMessage message = ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE.get( className, config.dn(), stackTraceToSingleLineString(e)); throw new ConfigException(message, e); } } /** * Determines whether or not the new configuration's implementation * class is acceptable. */ private boolean isJavaClassAcceptable( ConnectionHandlerCfg config, List<LocalizableMessage> unacceptableReasons) { String className = config.getJavaClass(); ConnectionHandlerCfgDefn d = ConnectionHandlerCfgDefn.getInstance(); ClassPropertyDefinition pd = d.getJavaClassPropertyDefinition(); try { ConnectionHandler<?> connectionHandler = connectionHandlers.get(config.dn()); if (connectionHandler == null) { @SuppressWarnings("rawtypes") Class<? extends ConnectionHandler> theClass = pd.loadClass(className, ConnectionHandler.class); connectionHandler = theClass.newInstance(); } return connectionHandler.isConfigurationAcceptable(config, unacceptableReasons); } catch (Exception e) { logger.traceException(e); unacceptableReasons.add(ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE.get( className, config.dn(), stackTraceToSingleLineString(e))); return false; } } }