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