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 legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2006-2008 Sun Microsystems, Inc. 025 * Portions Copyright 2014-2015 ForgeRock AS 026 */ 027package org.opends.server.core; 028 029import static org.opends.messages.ConfigMessages.*; 030import static org.opends.server.util.StaticUtils.*; 031 032import java.util.List; 033import java.util.concurrent.ConcurrentHashMap; 034 035import org.forgerock.i18n.LocalizableMessage; 036import org.forgerock.i18n.slf4j.LocalizedLogger; 037import org.forgerock.opendj.config.server.ConfigException; 038import org.opends.server.admin.ClassPropertyDefinition; 039import org.opends.server.admin.server.ConfigurationAddListener; 040import org.opends.server.admin.server.ConfigurationChangeListener; 041import org.opends.server.admin.server.ConfigurationDeleteListener; 042import org.opends.server.admin.server.ServerManagementContext; 043import org.opends.server.admin.std.meta.ExtendedOperationHandlerCfgDefn; 044import org.opends.server.admin.std.server.ExtendedOperationHandlerCfg; 045import org.opends.server.admin.std.server.RootCfg; 046import org.opends.server.api.ExtendedOperationHandler; 047import org.forgerock.opendj.config.server.ConfigChangeResult; 048import org.opends.server.types.DN; 049import org.opends.server.types.InitializationException; 050 051/** 052 * This class defines a utility that will be used to manage the set of extended 053 * operation handlers defined in the Directory Server. It will initialize the 054 * handlers when the server starts, and then will manage any additions, 055 * removals, or modifications of any extended operation handlers while the 056 * server is running. 057 */ 058public class ExtendedOperationConfigManager implements 059 ConfigurationChangeListener<ExtendedOperationHandlerCfg>, 060 ConfigurationAddListener<ExtendedOperationHandlerCfg>, 061 ConfigurationDeleteListener<ExtendedOperationHandlerCfg> 062{ 063 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 064 065 /** 066 * A mapping between the DNs of the config entries and the associated extended 067 * operation handlers. 068 */ 069 private final ConcurrentHashMap<DN,ExtendedOperationHandler> handlers; 070 071 private final ServerContext serverContext; 072 073 /** 074 * Creates a new instance of this extended operation config manager. 075 * 076 * @param serverContext 077 * The server context. 078 */ 079 public ExtendedOperationConfigManager(ServerContext serverContext) 080 { 081 this.serverContext = serverContext; 082 handlers = new ConcurrentHashMap<>(); 083 } 084 085 /** 086 * Initializes all extended operation handlers currently defined in the 087 * Directory Server configuration. This should only be called at Directory 088 * Server startup. 089 * 090 * @throws ConfigException If a configuration problem causes the extended 091 * operation handler initialization process to fail. 092 * 093 * @throws InitializationException If a problem occurs while initializing 094 * the extended operation handler that is 095 * not related to the server configuration. 096 */ 097 public void initializeExtendedOperationHandlers() 098 throws ConfigException, InitializationException 099 { 100 // Create an internal server management context and retrieve 101 // the root configuration which has the extended operation handler relation. 102 ServerManagementContext context = ServerManagementContext.getInstance(); 103 RootCfg root = context.getRootConfiguration(); 104 105 // Register add and delete listeners. 106 root.addExtendedOperationHandlerAddListener(this); 107 root.addExtendedOperationHandlerDeleteListener(this); 108 109 // Initialize existing handlers. 110 for (String name : root.listExtendedOperationHandlers()) 111 { 112 // Get the handler's configuration. 113 // This will decode and validate its properties. 114 ExtendedOperationHandlerCfg config = 115 root.getExtendedOperationHandler(name); 116 117 // Register as a change listener for this handler so that we can be 118 // notified when it is disabled or enabled. 119 config.addChangeListener(this); 120 121 // Ignore this handler if it is disabled. 122 if (config.isEnabled()) 123 { 124 // Load the handler's implementation class and initialize it. 125 ExtendedOperationHandler handler = getHandler(config); 126 127 // Put this handler in the hash map so that we will be able to find 128 // it if it is deleted or disabled. 129 handlers.put(config.dn(), handler); 130 } 131 } 132 } 133 134 /** {@inheritDoc} */ 135 @Override 136 public ConfigChangeResult applyConfigurationDelete( 137 ExtendedOperationHandlerCfg configuration) 138 { 139 final ConfigChangeResult ccr = new ConfigChangeResult(); 140 // See if the entry is registered as an extended operation handler. 141 // If so, deregister it and finalize the handler. 142 ExtendedOperationHandler handler = handlers.remove(configuration.dn()); 143 if (handler != null) 144 { 145 handler.finalizeExtendedOperationHandler(); 146 } 147 return ccr; 148 } 149 150 /** {@inheritDoc} */ 151 @Override 152 public boolean isConfigurationChangeAcceptable( 153 ExtendedOperationHandlerCfg configuration, 154 List<LocalizableMessage> unacceptableReasons) 155 { 156 return !configuration.isEnabled() 157 || isJavaClassAcceptable(configuration, unacceptableReasons); 158 } 159 160 /** {@inheritDoc} */ 161 @Override 162 public ConfigChangeResult applyConfigurationChange( 163 ExtendedOperationHandlerCfg configuration) 164 { 165 // Attempt to get the existing handler. This will only 166 // succeed if it was enabled. 167 DN dn = configuration.dn(); 168 ExtendedOperationHandler handler = handlers.get(dn); 169 170 final ConfigChangeResult ccr = new ConfigChangeResult(); 171 172 // See whether the handler should be enabled. 173 if (handler == null) { 174 if (configuration.isEnabled()) { 175 // The handler needs to be enabled. 176 try { 177 handler = getHandler(configuration); 178 179 // Put this handler in the hash so that we will 180 // be able to find it if it is altered. 181 handlers.put(dn, handler); 182 183 } catch (ConfigException e) { 184 logger.traceException(e); 185 186 ccr.addMessage(e.getMessageObject()); 187 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 188 } catch (Exception e) { 189 logger.traceException(e); 190 191 ccr.addMessage(ERR_CONFIG_EXTOP_INITIALIZATION_FAILED.get( 192 configuration.getJavaClass(), dn, stackTraceToSingleLineString(e))); 193 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 194 } 195 } 196 } else { 197 if (configuration.isEnabled()) { 198 // The handler is currently active, so we don't 199 // need to do anything. Changes to the class name cannot be 200 // applied dynamically, so if the class name did change then 201 // indicate that administrative action is required for that 202 // change to take effect. 203 String className = configuration.getJavaClass(); 204 if (!className.equals(handler.getClass().getName())) { 205 ccr.setAdminActionRequired(true); 206 } 207 } else { 208 // We need to disable the connection handler. 209 210 handlers.remove(dn); 211 212 handler.finalizeExtendedOperationHandler(); 213 } 214 } 215 216 return ccr; 217 } 218 219 /** {@inheritDoc} */ 220 @Override 221 public boolean isConfigurationAddAcceptable( 222 ExtendedOperationHandlerCfg configuration, 223 List<LocalizableMessage> unacceptableReasons) 224 { 225 return isConfigurationChangeAcceptable(configuration, unacceptableReasons); 226 } 227 228 /** {@inheritDoc} */ 229 @Override 230 public ConfigChangeResult applyConfigurationAdd( 231 ExtendedOperationHandlerCfg configuration) 232 { 233 final ConfigChangeResult ccr = new ConfigChangeResult(); 234 235 // Register as a change listener for this connection handler entry 236 // so that we will be notified of any changes that may be made to 237 // it. 238 configuration.addChangeListener(this); 239 240 // Ignore this connection handler if it is disabled. 241 if (configuration.isEnabled()) 242 { 243 // The connection handler needs to be enabled. 244 DN dn = configuration.dn(); 245 try { 246 ExtendedOperationHandler handler = getHandler(configuration); 247 248 // Put this connection handler in the hash so that we will be 249 // able to find it if it is altered. 250 handlers.put(dn, handler); 251 252 } 253 catch (ConfigException e) 254 { 255 logger.traceException(e); 256 257 ccr.addMessage(e.getMessageObject()); 258 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 259 } 260 catch (Exception e) 261 { 262 logger.traceException(e); 263 264 ccr.addMessage(ERR_CONFIG_EXTOP_INITIALIZATION_FAILED.get( 265 configuration.getJavaClass(), dn, stackTraceToSingleLineString(e))); 266 ccr.setResultCode(DirectoryServer.getServerErrorResultCode()); 267 } 268 } 269 270 return ccr; 271 } 272 273 /** {@inheritDoc} */ 274 @Override 275 public boolean isConfigurationDeleteAcceptable( 276 ExtendedOperationHandlerCfg configuration, 277 List<LocalizableMessage> unacceptableReasons) 278 { 279 // A delete should always be acceptable, so just return true. 280 return true; 281 } 282 283 /** Load and initialize the handler named in the config. */ 284 private ExtendedOperationHandler getHandler( 285 ExtendedOperationHandlerCfg config) throws ConfigException 286 { 287 String className = config.getJavaClass(); 288 ExtendedOperationHandlerCfgDefn d = 289 ExtendedOperationHandlerCfgDefn.getInstance(); 290 ClassPropertyDefinition pd = d.getJavaClassPropertyDefinition(); 291 292 try 293 { 294 Class<? extends ExtendedOperationHandler> theClass = 295 pd.loadClass(className, ExtendedOperationHandler.class); 296 ExtendedOperationHandler extendedOperationHandler = theClass.newInstance(); 297 298 extendedOperationHandler.initializeExtendedOperationHandler(config); 299 300 return extendedOperationHandler; 301 } 302 catch (Exception e) 303 { 304 logger.traceException(e); 305 throw new ConfigException(ERR_CONFIG_EXTOP_INVALID_CLASS.get(className, config.dn(), e), e); 306 } 307 } 308 309 310 311 /** 312 * Determines whether or not the new configuration's implementation 313 * class is acceptable. 314 */ 315 private boolean isJavaClassAcceptable(ExtendedOperationHandlerCfg config, 316 List<LocalizableMessage> unacceptableReasons) 317 { 318 String className = config.getJavaClass(); 319 ExtendedOperationHandlerCfgDefn d = 320 ExtendedOperationHandlerCfgDefn.getInstance(); 321 ClassPropertyDefinition pd = d.getJavaClassPropertyDefinition(); 322 323 try { 324 Class<? extends ExtendedOperationHandler> theClass = 325 pd.loadClass(className, ExtendedOperationHandler.class); 326 ExtendedOperationHandler extOpHandler = theClass.newInstance(); 327 328 return extOpHandler.isConfigurationAcceptable(config, unacceptableReasons); 329 } 330 catch (Exception e) 331 { 332 logger.traceException(e); 333 unacceptableReasons.add(ERR_CONFIG_EXTOP_INVALID_CLASS.get(className, config.dn(), e)); 334 return false; 335 } 336 } 337} 338