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