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 010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE 011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE. 012 * See the License for the specific language governing permissions 013 * and limitations under the License. 014 * 015 * When distributing Covered Code, include this CDDL HEADER in each 016 * file and include the License file at 017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, 018 * add the following below this CDDL HEADER, with the fields enclosed 019 * by brackets "[]" replaced with your own identifying information: 020 * Portions Copyright [yyyy] [name of copyright owner] 021 * 022 * CDDL HEADER END 023 * 024 * 025 * Copyright 2006-2008 Sun Microsystems, Inc. 026 */ 027 package org.opends.server.core; 028 import org.opends.messages.Message; 029 030 031 032 import static org.opends.server.loggers.debug.DebugLogger.*; 033 import org.opends.server.loggers.debug.DebugTracer; 034 import static org.opends.messages.ConfigMessages.*; 035 import static org.opends.messages.CoreMessages.*; 036 037 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString; 038 039 import java.lang.reflect.Method; 040 import java.util.ArrayList; 041 import java.util.List; 042 import java.util.concurrent.ConcurrentHashMap; 043 044 import org.opends.server.admin.ClassPropertyDefinition; 045 import org.opends.server.admin.server.ConfigurationAddListener; 046 import org.opends.server.admin.server.ConfigurationChangeListener; 047 import org.opends.server.admin.server.ConfigurationDeleteListener; 048 import org.opends.server.admin.server.ServerManagementContext; 049 import org.opends.server.admin.std.meta.*; 050 import org.opends.server.admin.std.server.ConnectionHandlerCfg; 051 import org.opends.server.admin.std.server.RootCfg; 052 import org.opends.server.api.ConnectionHandler; 053 import org.opends.server.config.ConfigException; 054 import org.opends.server.types.ConfigChangeResult; 055 import org.opends.server.types.DN; 056 import org.opends.server.types.DebugLogLevel; 057 import org.opends.server.types.InitializationException; 058 import org.opends.server.types.ResultCode; 059 060 061 /** 062 * This class defines a utility that will be used to manage the 063 * configuration for the set of connection handlers defined in the 064 * Directory Server. It will perform the necessary initialization of 065 * those connection handlers when the server is first started, and 066 * then will manage any changes to them while the server is running. 067 */ 068 public class ConnectionHandlerConfigManager implements 069 ConfigurationAddListener<ConnectionHandlerCfg>, 070 ConfigurationDeleteListener<ConnectionHandlerCfg>, 071 ConfigurationChangeListener<ConnectionHandlerCfg> { 072 /** 073 * The tracer object for the debug logger. 074 */ 075 private static final DebugTracer TRACER = getTracer(); 076 077 078 // The mapping between configuration entry DNs and their 079 // corresponding connection handler implementations. 080 private ConcurrentHashMap<DN, ConnectionHandler> connectionHandlers; 081 082 083 084 /** 085 * Creates a new instance of this connection handler config manager. 086 */ 087 public ConnectionHandlerConfigManager() { 088 // No implementation is required. 089 } 090 091 092 093 /** 094 * {@inheritDoc} 095 */ 096 public ConfigChangeResult applyConfigurationAdd( 097 ConnectionHandlerCfg configuration) { 098 // Default result code. 099 ResultCode resultCode = ResultCode.SUCCESS; 100 boolean adminActionRequired = false; 101 ArrayList<Message> messages = new ArrayList<Message>(); 102 103 // Register as a change listener for this connection handler entry 104 // so that we will be notified of any changes that may be made to 105 // it. 106 configuration.addChangeListener(this); 107 108 // Ignore this connection handler if it is disabled. 109 if (configuration.isEnabled()) { 110 // The connection handler needs to be enabled. 111 DN dn = configuration.dn(); 112 try { 113 // Attempt to start the connection handler. 114 ConnectionHandler<? extends ConnectionHandlerCfg> connectionHandler = 115 getConnectionHandler(configuration); 116 connectionHandler.start(); 117 118 // Put this connection handler in the hash so that we will be 119 // able to find it if it is altered. 120 connectionHandlers.put(dn, connectionHandler); 121 122 // Register the connection handler with the Directory Server. 123 DirectoryServer.registerConnectionHandler(connectionHandler); 124 } catch (ConfigException e) { 125 if (debugEnabled()) 126 { 127 TRACER.debugCaught(DebugLogLevel.ERROR, e); 128 } 129 130 messages.add(e.getMessageObject()); 131 resultCode = DirectoryServer.getServerErrorResultCode(); 132 } catch (Exception e) { 133 if (debugEnabled()) 134 { 135 TRACER.debugCaught(DebugLogLevel.ERROR, e); 136 } 137 138 139 messages.add(ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE.get( 140 String.valueOf(configuration.getJavaClass()), 141 String.valueOf(dn), 142 stackTraceToSingleLineString(e))); 143 resultCode = DirectoryServer.getServerErrorResultCode(); 144 } 145 } 146 147 // Return the configuration result. 148 return new ConfigChangeResult(resultCode, adminActionRequired, 149 messages); 150 } 151 152 153 154 /** 155 * {@inheritDoc} 156 */ 157 public ConfigChangeResult applyConfigurationChange( 158 ConnectionHandlerCfg configuration) { 159 // Attempt to get the existing connection handler. This will only 160 // succeed if it was enabled. 161 DN dn = configuration.dn(); 162 ConnectionHandler connectionHandler = connectionHandlers.get(dn); 163 164 // Default result code. 165 ResultCode resultCode = ResultCode.SUCCESS; 166 boolean adminActionRequired = false; 167 ArrayList<Message> messages = new ArrayList<Message>(); 168 169 // See whether the connection handler should be enabled. 170 if (connectionHandler == null) { 171 if (configuration.isEnabled()) { 172 // The connection handler needs to be enabled. 173 try { 174 // Attempt to start the connection handler. 175 connectionHandler = getConnectionHandler(configuration); 176 connectionHandler.start(); 177 178 // Put this connection handler in the hash so that we will 179 // be able to find it if it is altered. 180 connectionHandlers.put(dn, connectionHandler); 181 182 // Register the connection handler with the Directory 183 // Server. 184 DirectoryServer.registerConnectionHandler( 185 (ConnectionHandler<? extends ConnectionHandlerCfg>) 186 connectionHandler); 187 } catch (ConfigException e) { 188 if (debugEnabled()) 189 { 190 TRACER.debugCaught(DebugLogLevel.ERROR, e); 191 } 192 193 messages.add(e.getMessageObject()); 194 resultCode = DirectoryServer.getServerErrorResultCode(); 195 } catch (Exception e) { 196 if (debugEnabled()) 197 { 198 TRACER.debugCaught(DebugLogLevel.ERROR, e); 199 } 200 201 messages.add(ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE.get( 202 String.valueOf(configuration 203 .getJavaClass()), String.valueOf(dn), 204 stackTraceToSingleLineString(e))); 205 resultCode = DirectoryServer.getServerErrorResultCode(); 206 } 207 } 208 } else { 209 if (configuration.isEnabled()) { 210 // The connection handler is currently active, so we don't 211 // need to do anything. Changes to the class name cannot be 212 // applied dynamically, so if the class name did change then 213 // indicate that administrative action is required for that 214 // change to take effect. 215 String className = configuration.getJavaClass(); 216 if (!className.equals(connectionHandler.getClass().getName())) { 217 adminActionRequired = true; 218 } 219 } else { 220 // We need to disable the connection handler. 221 DirectoryServer 222 .deregisterConnectionHandler(connectionHandler); 223 connectionHandlers.remove(dn); 224 225 226 connectionHandler.finalizeConnectionHandler( 227 INFO_CONNHANDLER_CLOSED_BY_DISABLE.get(), false); 228 } 229 } 230 231 // Return the configuration result. 232 return new ConfigChangeResult(resultCode, adminActionRequired, 233 messages); 234 } 235 236 237 238 /** 239 * {@inheritDoc} 240 */ 241 public ConfigChangeResult applyConfigurationDelete( 242 ConnectionHandlerCfg configuration) { 243 244 // Default result code. 245 ResultCode resultCode = ResultCode.SUCCESS; 246 boolean adminActionRequired = false; 247 248 // See if the entry is registered as a connection handler. If so, 249 // deregister and stop it. We'll try to leave any established 250 // connections alone if possible. 251 DN dn = configuration.dn(); 252 ConnectionHandler connectionHandler = connectionHandlers.get(dn); 253 if (connectionHandler != null) { 254 DirectoryServer.deregisterConnectionHandler(connectionHandler); 255 connectionHandlers.remove(dn); 256 257 connectionHandler.finalizeConnectionHandler( 258 INFO_CONNHANDLER_CLOSED_BY_DELETE.get(), 259 false); 260 } 261 262 return new ConfigChangeResult(resultCode, adminActionRequired); 263 } 264 265 266 267 /** 268 * Initializes the configuration associated with the Directory 269 * Server connection handlers. This should only be called at 270 * Directory Server startup. 271 * 272 * @throws ConfigException 273 * If a critical configuration problem prevents the 274 * connection handler initialization from succeeding. 275 * @throws InitializationException 276 * If a problem occurs while initializing the connection 277 * handlers that is not related to the server 278 * configuration. 279 */ 280 public void initializeConnectionHandlerConfig() 281 throws ConfigException, InitializationException { 282 connectionHandlers = new ConcurrentHashMap<DN, ConnectionHandler>(); 283 284 // Get the root configuration which acts as the parent of all 285 // connection handlers. 286 ServerManagementContext context = ServerManagementContext 287 .getInstance(); 288 RootCfg root = context.getRootConfiguration(); 289 290 // Register as an add and delete listener so that we can 291 // be notified if new connection handlers are added or existing 292 // connection handlers are removed. 293 root.addConnectionHandlerAddListener(this); 294 root.addConnectionHandlerDeleteListener(this); 295 296 // Initialize existing connection handles. 297 for (String name : root.listConnectionHandlers()) { 298 ConnectionHandlerCfg config = root 299 .getConnectionHandler(name); 300 301 // Register as a change listener for this connection handler 302 // entry so that we will be notified of any changes that may be 303 // made to it. 304 config.addChangeListener(this); 305 306 // Ignore this connection handler if it is disabled. 307 if (config.isEnabled()) { 308 // Note that we don't want to start the connection handler 309 // because we're still in the startup process. Therefore, we 310 // will not do so and allow the server to start it at the very 311 // end of the initialization process. 312 ConnectionHandler<? extends ConnectionHandlerCfg> connectionHandler = 313 getConnectionHandler(config); 314 315 // Put this connection handler in the hash so that we will be 316 // able to find it if it is altered. 317 connectionHandlers.put(config.dn(), connectionHandler); 318 319 // Register the connection handler with the Directory Server. 320 DirectoryServer.registerConnectionHandler(connectionHandler); 321 } 322 } 323 } 324 325 326 327 /** 328 * {@inheritDoc} 329 */ 330 public boolean isConfigurationAddAcceptable( 331 ConnectionHandlerCfg configuration, 332 List<Message> unacceptableReasons) { 333 if (configuration.isEnabled()) { 334 // It's enabled so always validate the class. 335 return isJavaClassAcceptable(configuration, unacceptableReasons); 336 } else { 337 // It's disabled so ignore it. 338 return true; 339 } 340 } 341 342 343 344 /** 345 * {@inheritDoc} 346 */ 347 public boolean isConfigurationChangeAcceptable( 348 ConnectionHandlerCfg configuration, 349 List<Message> unacceptableReasons) { 350 if (configuration.isEnabled()) { 351 // It's enabled so always validate the class. 352 return isJavaClassAcceptable(configuration, unacceptableReasons); 353 } else { 354 // It's disabled so ignore it. 355 return true; 356 } 357 } 358 359 360 361 /** 362 * {@inheritDoc} 363 */ 364 public boolean isConfigurationDeleteAcceptable( 365 ConnectionHandlerCfg configuration, 366 List<Message> unacceptableReasons) { 367 // A delete should always be acceptable, so just return true. 368 return true; 369 } 370 371 372 373 // Load and initialize the connection handler named in the config. 374 private ConnectionHandler<? extends ConnectionHandlerCfg> 375 getConnectionHandler(ConnectionHandlerCfg config) 376 throws ConfigException 377 { 378 String className = config.getJavaClass(); 379 ConnectionHandlerCfgDefn d = 380 ConnectionHandlerCfgDefn.getInstance(); 381 ClassPropertyDefinition pd = d 382 .getJavaClassPropertyDefinition(); 383 384 // Load the class and cast it to a connection handler. 385 Class<? extends ConnectionHandler> theClass; 386 ConnectionHandler connectionHandler; 387 388 try { 389 theClass = pd.loadClass(className, ConnectionHandler.class); 390 connectionHandler = theClass.newInstance(); 391 } catch (Exception e) { 392 if (debugEnabled()) 393 { 394 TRACER.debugCaught(DebugLogLevel.ERROR, e); 395 } 396 397 Message message = ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE. 398 get(String.valueOf(className), String.valueOf(config.dn()), 399 stackTraceToSingleLineString(e)); 400 throw new ConfigException(message, e); 401 } 402 403 // Perform the necessary initialization for the connection 404 // handler. 405 try { 406 // Determine the initialization method to use: it must take a 407 // single parameter which is the exact type of the configuration 408 // object. 409 Method method = theClass.getMethod("initializeConnectionHandler", config 410 .configurationClass()); 411 412 method.invoke(connectionHandler, config); 413 } catch (Exception e) { 414 if (debugEnabled()) 415 { 416 TRACER.debugCaught(DebugLogLevel.ERROR, e); 417 } 418 419 Message message = ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE. 420 get(String.valueOf(className), String.valueOf(config.dn()), 421 stackTraceToSingleLineString(e)); 422 throw new ConfigException(message, e); 423 } 424 425 // The connection handler has been successfully initialized. 426 return (ConnectionHandler<? extends ConnectionHandlerCfg>) 427 connectionHandler; 428 } 429 430 431 432 // Determines whether or not the new configuration's implementation 433 // class is acceptable. 434 private boolean isJavaClassAcceptable( 435 ConnectionHandlerCfg config, 436 List<Message> unacceptableReasons) { 437 String className = config.getJavaClass(); 438 ConnectionHandlerCfgDefn d = 439 ConnectionHandlerCfgDefn.getInstance(); 440 ClassPropertyDefinition pd = d 441 .getJavaClassPropertyDefinition(); 442 443 // Load the class and cast it to a connection handler. 444 ConnectionHandler connectionHandler = null; 445 Class<? extends ConnectionHandler> theClass; 446 try { 447 connectionHandler = connectionHandlers.get(config.dn()); 448 theClass = pd.loadClass(className, ConnectionHandler.class); 449 if (connectionHandler == null) { 450 connectionHandler = theClass.newInstance(); 451 } 452 } catch (Exception e) { 453 if (debugEnabled()) 454 { 455 TRACER.debugCaught(DebugLogLevel.ERROR, e); 456 } 457 458 unacceptableReasons.add( 459 ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE.get( 460 String.valueOf(className), 461 String.valueOf(config.dn()), 462 stackTraceToSingleLineString(e))); 463 return false; 464 } 465 466 // Perform the necessary initialization for the connection 467 // handler. 468 try { 469 // Determine the initialization method to use: it must take a 470 // single parameter which is the exact type of the configuration 471 // object. 472 Method method = theClass.getMethod("isConfigurationAcceptable", 473 ConnectionHandlerCfg.class, 474 List.class); 475 Boolean acceptable = (Boolean) method.invoke(connectionHandler, config, 476 unacceptableReasons); 477 478 if (! acceptable) 479 { 480 return false; 481 } 482 } catch (Exception e) { 483 if (debugEnabled()) 484 { 485 TRACER.debugCaught(DebugLogLevel.ERROR, e); 486 } 487 488 unacceptableReasons.add(ERR_CONFIG_CONNHANDLER_CANNOT_INITIALIZE.get( 489 String.valueOf(className), String.valueOf(config.dn()), 490 stackTraceToSingleLineString(e))); 491 return false; 492 } 493 494 // The class is valid as far as we can tell. 495 return true; 496 } 497 }