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 java.util.ArrayList; 033 import java.util.List; 034 import java.util.concurrent.ConcurrentHashMap; 035 import java.lang.reflect.Method; 036 037 import org.opends.server.admin.ClassPropertyDefinition; 038 import org.opends.server.admin.server.ConfigurationChangeListener; 039 import org.opends.server.admin.server.ConfigurationAddListener; 040 import org.opends.server.admin.server.ConfigurationDeleteListener; 041 import org.opends.server.admin.server.ServerManagementContext; 042 import org.opends.server.admin.std.server.ExtendedOperationHandlerCfg; 043 import org.opends.server.admin.std.server.RootCfg; 044 import org.opends.server.admin.std.meta.ExtendedOperationHandlerCfgDefn; 045 import org.opends.server.api.ExtendedOperationHandler; 046 import org.opends.server.config.ConfigException; 047 import org.opends.server.loggers.debug.DebugTracer; 048 import org.opends.server.types.ConfigChangeResult; 049 import org.opends.server.types.DebugLogLevel; 050 import org.opends.server.types.DN; 051 import org.opends.server.types.InitializationException; 052 import org.opends.server.types.ResultCode; 053 054 import static org.opends.server.loggers.debug.DebugLogger.*; 055 import static org.opends.messages.ConfigMessages.*; 056 057 import static org.opends.server.util.StaticUtils.stackTraceToSingleLineString; 058 059 060 061 /** 062 * This class defines a utility that will be used to manage the set of extended 063 * operation handlers defined in the Directory Server. It will initialize the 064 * handlers when the server starts, and then will manage any additions, 065 * removals, or modifications of any extended operation handlers while the 066 * server is running. 067 */ 068 public class ExtendedOperationConfigManager implements 069 ConfigurationChangeListener<ExtendedOperationHandlerCfg>, 070 ConfigurationAddListener<ExtendedOperationHandlerCfg>, 071 ConfigurationDeleteListener<ExtendedOperationHandlerCfg> 072 { 073 /** 074 * The tracer object for the debug logger. 075 */ 076 private static final DebugTracer TRACER = getTracer(); 077 078 079 080 // A mapping between the DNs of the config entries and the associated extended 081 // operation handlers. 082 private ConcurrentHashMap<DN,ExtendedOperationHandler> handlers; 083 084 085 086 /** 087 * Creates a new instance of this extended operation config manager. 088 */ 089 public ExtendedOperationConfigManager() 090 { 091 handlers = new ConcurrentHashMap<DN,ExtendedOperationHandler>(); 092 } 093 094 095 096 /** 097 * Initializes all extended operation handlers currently defined in the 098 * Directory Server configuration. This should only be called at Directory 099 * Server startup. 100 * 101 * @throws ConfigException If a configuration problem causes the extended 102 * operation handler initialization process to fail. 103 * 104 * @throws InitializationException If a problem occurs while initializing 105 * the extended operation handler that is 106 * not related to the server configuration. 107 */ 108 public void initializeExtendedOperationHandlers() 109 throws ConfigException, InitializationException 110 { 111 // Create an internal server management context and retrieve 112 // the root configuration which has the extended operation handler relation. 113 ServerManagementContext context = ServerManagementContext.getInstance(); 114 RootCfg root = context.getRootConfiguration(); 115 116 // Register add and delete listeners. 117 root.addExtendedOperationHandlerAddListener(this); 118 root.addExtendedOperationHandlerDeleteListener(this); 119 120 // Initialize existing handlers. 121 for (String name : root.listExtendedOperationHandlers()) 122 { 123 // Get the handler's configuration. 124 // This will decode and validate its properties. 125 ExtendedOperationHandlerCfg config = 126 root.getExtendedOperationHandler(name); 127 128 // Register as a change listener for this handler so that we can be 129 // notified when it is disabled or enabled. 130 config.addChangeListener(this); 131 132 // Ignore this handler if it is disabled. 133 if (config.isEnabled()) 134 { 135 // Load the handler's implementation class and initialize it. 136 ExtendedOperationHandler handler = getHandler(config); 137 138 // Put this handler in the hash map so that we will be able to find 139 // it if it is deleted or disabled. 140 handlers.put(config.dn(), handler); 141 } 142 } 143 } 144 145 /** 146 * {@inheritDoc} 147 */ 148 public ConfigChangeResult applyConfigurationDelete( 149 ExtendedOperationHandlerCfg configuration) 150 { 151 ResultCode resultCode = ResultCode.SUCCESS; 152 boolean adminActionRequired = false; 153 154 155 // See if the entry is registered as an extended operation handler. If so, 156 // deregister it and finalize the handler. 157 ExtendedOperationHandler handler = handlers.remove(configuration.dn()); 158 if (handler != null) 159 { 160 handler.finalizeExtendedOperationHandler(); 161 } 162 163 164 return new ConfigChangeResult(resultCode, adminActionRequired); 165 } 166 167 /** 168 * {@inheritDoc} 169 */ 170 public boolean isConfigurationChangeAcceptable( 171 ExtendedOperationHandlerCfg configuration, 172 List<Message> unacceptableReasons) 173 { 174 if (configuration.isEnabled()) { 175 // It's enabled so always validate the class. 176 return isJavaClassAcceptable(configuration, unacceptableReasons); 177 } else { 178 // It's disabled so ignore it. 179 return true; 180 } 181 } 182 183 /** 184 * {@inheritDoc} 185 */ 186 public ConfigChangeResult applyConfigurationChange( 187 ExtendedOperationHandlerCfg configuration) 188 { 189 // Attempt to get the existing handler. This will only 190 // succeed if it was enabled. 191 DN dn = configuration.dn(); 192 ExtendedOperationHandler handler = handlers.get(dn); 193 194 // Default result code. 195 ResultCode resultCode = ResultCode.SUCCESS; 196 boolean adminActionRequired = false; 197 ArrayList<Message> messages = new ArrayList<Message>(); 198 199 // See whether the handler should be enabled. 200 if (handler == null) { 201 if (configuration.isEnabled()) { 202 // The handler needs to be enabled. 203 try { 204 handler = getHandler(configuration); 205 206 // Put this handler in the hash so that we will 207 // be able to find it if it is altered. 208 handlers.put(dn, handler); 209 210 } catch (ConfigException e) { 211 if (debugEnabled()) 212 { 213 TRACER.debugCaught(DebugLogLevel.ERROR, e); 214 } 215 216 messages.add(e.getMessageObject()); 217 resultCode = DirectoryServer.getServerErrorResultCode(); 218 } catch (Exception e) { 219 if (debugEnabled()) 220 { 221 TRACER.debugCaught(DebugLogLevel.ERROR, e); 222 } 223 224 messages.add(ERR_CONFIG_EXTOP_INITIALIZATION_FAILED.get( 225 String.valueOf(configuration.getJavaClass()), 226 String.valueOf(dn), 227 stackTraceToSingleLineString(e))); 228 resultCode = DirectoryServer.getServerErrorResultCode(); 229 } 230 } 231 } else { 232 if (configuration.isEnabled()) { 233 // The handler is currently active, so we don't 234 // need to do anything. Changes to the class name cannot be 235 // applied dynamically, so if the class name did change then 236 // indicate that administrative action is required for that 237 // change to take effect. 238 String className = configuration.getJavaClass(); 239 if (!className.equals(handler.getClass().getName())) { 240 adminActionRequired = true; 241 } 242 } else { 243 // We need to disable the connection handler. 244 245 handlers.remove(dn); 246 247 handler.finalizeExtendedOperationHandler(); 248 } 249 } 250 251 // Return the configuration result. 252 return new ConfigChangeResult(resultCode, adminActionRequired, 253 messages); 254 } 255 256 /** 257 * {@inheritDoc} 258 */ 259 public boolean isConfigurationAddAcceptable( 260 ExtendedOperationHandlerCfg configuration, 261 List<Message> unacceptableReasons) 262 { 263 return isConfigurationChangeAcceptable(configuration, unacceptableReasons); 264 } 265 266 /** 267 * {@inheritDoc} 268 */ 269 public ConfigChangeResult applyConfigurationAdd( 270 ExtendedOperationHandlerCfg configuration) 271 { 272 // Default result code. 273 ResultCode resultCode = ResultCode.SUCCESS; 274 boolean adminActionRequired = false; 275 ArrayList<Message> messages = new ArrayList<Message>(); 276 277 // Register as a change listener for this connection handler entry 278 // so that we will be notified of any changes that may be made to 279 // it. 280 configuration.addChangeListener(this); 281 282 // Ignore this connection handler if it is disabled. 283 if (configuration.isEnabled()) 284 { 285 // The connection handler needs to be enabled. 286 DN dn = configuration.dn(); 287 try { 288 ExtendedOperationHandler handler = getHandler(configuration); 289 290 // Put this connection handler in the hash so that we will be 291 // able to find it if it is altered. 292 handlers.put(dn, handler); 293 294 } 295 catch (ConfigException e) 296 { 297 if (debugEnabled()) 298 { 299 TRACER.debugCaught(DebugLogLevel.ERROR, e); 300 } 301 302 messages.add(e.getMessageObject()); 303 resultCode = DirectoryServer.getServerErrorResultCode(); 304 } 305 catch (Exception e) 306 { 307 if (debugEnabled()) 308 { 309 TRACER.debugCaught(DebugLogLevel.ERROR, e); 310 } 311 312 messages.add(ERR_CONFIG_EXTOP_INITIALIZATION_FAILED.get( 313 String.valueOf(configuration.getJavaClass()), 314 String.valueOf(dn), 315 stackTraceToSingleLineString(e))); 316 resultCode = DirectoryServer.getServerErrorResultCode(); 317 } 318 } 319 320 // Return the configuration result. 321 return new ConfigChangeResult(resultCode, adminActionRequired, 322 messages); 323 } 324 325 /** 326 * {@inheritDoc} 327 */ 328 public boolean isConfigurationDeleteAcceptable( 329 ExtendedOperationHandlerCfg configuration, 330 List<Message> unacceptableReasons) 331 { 332 // A delete should always be acceptable, so just return true. 333 return true; 334 } 335 336 // Load and initialize the handler named in the config. 337 private ExtendedOperationHandler getHandler( 338 ExtendedOperationHandlerCfg config) throws ConfigException 339 { 340 String className = config.getJavaClass(); 341 ExtendedOperationHandlerCfgDefn d = 342 ExtendedOperationHandlerCfgDefn.getInstance(); 343 ClassPropertyDefinition pd = d 344 .getJavaClassPropertyDefinition(); 345 346 // Load the class and cast it to an extended operation handler. 347 Class<? extends ExtendedOperationHandler> theClass; 348 ExtendedOperationHandler extendedOperationHandler; 349 350 try 351 { 352 theClass = pd.loadClass(className, ExtendedOperationHandler.class); 353 extendedOperationHandler = theClass.newInstance(); 354 355 // Determine the initialization method to use: it must take a 356 // single parameter which is the exact type of the configuration 357 // object. 358 Method method = theClass.getMethod("initializeExtendedOperationHandler", 359 config.configurationClass()); 360 361 method.invoke(extendedOperationHandler, config); 362 } 363 catch (Exception e) 364 { 365 if (debugEnabled()) 366 { 367 TRACER.debugCaught(DebugLogLevel.ERROR, e); 368 } 369 370 Message message = ERR_CONFIG_EXTOP_INVALID_CLASS. 371 get(String.valueOf(className), String.valueOf(config.dn()), 372 String.valueOf(e)); 373 throw new ConfigException(message, e); 374 } 375 376 // The handler has been successfully initialized. 377 return extendedOperationHandler; 378 } 379 380 381 382 // Determines whether or not the new configuration's implementation 383 // class is acceptable. 384 private boolean isJavaClassAcceptable(ExtendedOperationHandlerCfg config, 385 List<Message> unacceptableReasons) 386 { 387 String className = config.getJavaClass(); 388 ExtendedOperationHandlerCfgDefn d = 389 ExtendedOperationHandlerCfgDefn.getInstance(); 390 ClassPropertyDefinition pd = d 391 .getJavaClassPropertyDefinition(); 392 393 // Load the class and cast it to an extended operation handler. 394 Class<? extends ExtendedOperationHandler> theClass; 395 try { 396 theClass = pd.loadClass(className, ExtendedOperationHandler.class); 397 ExtendedOperationHandler extOpHandler = theClass.newInstance(); 398 399 // Determine the initialization method to use: it must take a 400 // single parameter which is the exact type of the configuration 401 // object. 402 Method method = theClass.getMethod("isConfigurationAcceptable", 403 ExtendedOperationHandlerCfg.class, 404 List.class); 405 Boolean acceptable = (Boolean) method.invoke(extOpHandler, config, 406 unacceptableReasons); 407 408 if (! acceptable) 409 { 410 return false; 411 } 412 } 413 catch (Exception e) 414 { 415 if (debugEnabled()) 416 { 417 TRACER.debugCaught(DebugLogLevel.ERROR, e); 418 } 419 420 unacceptableReasons.add(ERR_CONFIG_EXTOP_INVALID_CLASS.get(className, 421 String.valueOf(config.dn()), 422 String.valueOf(e))); 423 return false; 424 } 425 426 // The class is valid as far as we can tell. 427 return true; 428 } 429 } 430