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 java.util.ArrayList; 020import java.util.List; 021import java.util.concurrent.ConcurrentHashMap; 022 023import org.forgerock.i18n.LocalizableMessage; 024import org.forgerock.i18n.slf4j.LocalizedLogger; 025import org.forgerock.opendj.ldap.ResultCode; 026import org.forgerock.util.Utils; 027import org.opends.server.admin.ClassPropertyDefinition; 028import org.opends.server.admin.server.ConfigurationAddListener; 029import org.opends.server.admin.server.ConfigurationChangeListener; 030import org.opends.server.admin.server.ConfigurationDeleteListener; 031import org.opends.server.admin.server.ServerManagementContext; 032import org.opends.server.admin.std.meta.AlertHandlerCfgDefn; 033import org.opends.server.admin.std.server.AlertHandlerCfg; 034import org.opends.server.admin.std.server.RootCfg; 035import org.opends.server.api.AlertHandler; 036import org.forgerock.opendj.config.server.ConfigException; 037import org.forgerock.opendj.config.server.ConfigChangeResult; 038import org.forgerock.opendj.ldap.DN; 039import org.opends.server.types.InitializationException; 040 041import static org.opends.messages.ConfigMessages.*; 042import static org.opends.server.util.StaticUtils.*; 043 044/** 045 * This class defines a utility that will be used to manage the set of alert 046 * handlers defined in the Directory Server. It will initialize the alert 047 * handlers when the server starts, and then will manage any additions, 048 * removals, or modifications to any alert handlers while the server is running. 049 */ 050public class AlertHandlerConfigManager 051 implements ConfigurationChangeListener<AlertHandlerCfg>, 052 ConfigurationAddListener<AlertHandlerCfg>, 053 ConfigurationDeleteListener<AlertHandlerCfg> 054 055{ 056 057 private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass(); 058 059 /** A mapping between the DNs of the config entries and the associated alert handlers. */ 060 private final ConcurrentHashMap<DN,AlertHandler> alertHandlers; 061 062 private final ServerContext serverContext; 063 064 /** 065 * Creates a new instance of this alert handler config manager. 066 * 067 * @param serverContext 068 * The server context. 069 */ 070 public AlertHandlerConfigManager(ServerContext serverContext) 071 { 072 this.serverContext = serverContext; 073 alertHandlers = new ConcurrentHashMap<>(); 074 } 075 076 /** 077 * Initializes all alert handlers currently defined in the Directory Server 078 * configuration. This should only be called at Directory Server startup. 079 * 080 * @throws ConfigException If a configuration problem causes the alert 081 * handler initialization process to fail. 082 * 083 * @throws InitializationException If a problem occurs while initializing 084 * the alert handlers that is not related to 085 * the server configuration. 086 */ 087 public void initializeAlertHandlers() 088 throws ConfigException, InitializationException 089 { 090 // Get the root configuration object. 091 ServerManagementContext managementContext = 092 ServerManagementContext.getInstance(); 093 RootCfg rootConfiguration = managementContext.getRootConfiguration(); 094 095 096 // Register as an add and delete listener with the root configuration so we 097 // can be notified if any alert handler entries are added or removed. 098 rootConfiguration.addAlertHandlerAddListener(this); 099 rootConfiguration.addAlertHandlerDeleteListener(this); 100 101 102 //Initialize the existing alert handlers. 103 for (String name : rootConfiguration.listAlertHandlers()) 104 { 105 AlertHandlerCfg configuration = rootConfiguration.getAlertHandler(name); 106 configuration.addChangeListener(this); 107 108 if (configuration.isEnabled()) 109 { 110 String className = configuration.getJavaClass(); 111 try 112 { 113 AlertHandler handler = loadHandler(className, configuration, true); 114 alertHandlers.put(configuration.dn(), handler); 115 DirectoryServer.registerAlertHandler(handler); 116 } 117 catch (InitializationException ie) 118 { 119 logger.error(ie.getMessageObject()); 120 continue; 121 } 122 } 123 } 124 } 125 126 127 128 /** {@inheritDoc} */ 129 @Override 130 public boolean isConfigurationAddAcceptable(AlertHandlerCfg configuration, 131 List<LocalizableMessage> unacceptableReasons) 132 { 133 if (configuration.isEnabled()) 134 { 135 // Get the name of the class and make sure we can instantiate it as an 136 // alert handler. 137 String className = configuration.getJavaClass(); 138 try 139 { 140 loadHandler(className, configuration, false); 141 } 142 catch (InitializationException ie) 143 { 144 unacceptableReasons.add(ie.getMessageObject()); 145 return false; 146 } 147 } 148 149 // If we've gotten here, then it's fine. 150 return true; 151 } 152 153 154 155 /** {@inheritDoc} */ 156 @Override 157 public ConfigChangeResult applyConfigurationAdd(AlertHandlerCfg configuration) 158 { 159 final ConfigChangeResult ccr = new ConfigChangeResult(); 160 161 configuration.addChangeListener(this); 162 163 if (! configuration.isEnabled()) 164 { 165 return ccr; 166 } 167 168 AlertHandler alertHandler = null; 169 170 // Get the name of the class and make sure we can instantiate it as an alert 171 // handler. 172 String className = configuration.getJavaClass(); 173 try 174 { 175 alertHandler = loadHandler(className, configuration, true); 176 } 177 catch (InitializationException ie) 178 { 179 ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode()); 180 ccr.addMessage(ie.getMessageObject()); 181 } 182 183 if (ccr.getResultCode() == ResultCode.SUCCESS) 184 { 185 alertHandlers.put(configuration.dn(), alertHandler); 186 DirectoryServer.registerAlertHandler(alertHandler); 187 } 188 189 return ccr; 190 } 191 192 193 194 /** {@inheritDoc} */ 195 @Override 196 public boolean isConfigurationDeleteAcceptable( 197 AlertHandlerCfg configuration, 198 List<LocalizableMessage> unacceptableReasons) 199 { 200 // FIXME -- We should try to perform some check to determine whether the 201 // alert handler is in use. 202 return true; 203 } 204 205 206 207 /** {@inheritDoc} */ 208 @Override 209 public ConfigChangeResult applyConfigurationDelete( 210 AlertHandlerCfg configuration) 211 { 212 final ConfigChangeResult ccr = new ConfigChangeResult(); 213 214 AlertHandler alertHandler = alertHandlers.remove(configuration.dn()); 215 if (alertHandler != null) 216 { 217 DirectoryServer.deregisterAlertHandler(alertHandler); 218 alertHandler.finalizeAlertHandler(); 219 } 220 221 return ccr; 222 } 223 224 225 226 /** {@inheritDoc} */ 227 @Override 228 public boolean isConfigurationChangeAcceptable(AlertHandlerCfg configuration, 229 List<LocalizableMessage> unacceptableReasons) 230 { 231 if (configuration.isEnabled()) 232 { 233 // Get the name of the class and make sure we can instantiate it as an 234 // alert handler. 235 String className = configuration.getJavaClass(); 236 try 237 { 238 loadHandler(className, configuration, false); 239 } 240 catch (InitializationException ie) 241 { 242 unacceptableReasons.add(ie.getMessageObject()); 243 return false; 244 } 245 } 246 247 // If we've gotten here, then it's fine. 248 return true; 249 } 250 251 252 253 /** {@inheritDoc} */ 254 @Override 255 public ConfigChangeResult applyConfigurationChange( 256 AlertHandlerCfg configuration) 257 { 258 final ConfigChangeResult ccr = new ConfigChangeResult(); 259 260 261 // Get the existing alert handler if it's already enabled. 262 AlertHandler existingHandler = alertHandlers.get(configuration.dn()); 263 264 265 // If the new configuration has the handler disabled, then disable it if it 266 // is enabled, or do nothing if it's already disabled. 267 if (! configuration.isEnabled()) 268 { 269 if (existingHandler != null) 270 { 271 DirectoryServer.deregisterAlertHandler(existingHandler); 272 273 AlertHandler alertHandler = alertHandlers.remove(configuration.dn()); 274 if (alertHandler != null) 275 { 276 alertHandler.finalizeAlertHandler(); 277 } 278 } 279 280 return ccr; 281 } 282 283 284 // Get the class for the alert handler. If the handler is already enabled, 285 // then we shouldn't do anything with it although if the class has changed 286 // then we'll at least need to indicate that administrative action is 287 // required. If the handler is disabled, then instantiate the class and 288 // initialize and register it as an alert handler. 289 String className = configuration.getJavaClass(); 290 if (existingHandler != null) 291 { 292 if (! className.equals(existingHandler.getClass().getName())) 293 { 294 ccr.setAdminActionRequired(true); 295 } 296 297 return ccr; 298 } 299 300 AlertHandler alertHandler = null; 301 try 302 { 303 alertHandler = loadHandler(className, configuration, true); 304 } 305 catch (InitializationException ie) 306 { 307 ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode()); 308 ccr.addMessage(ie.getMessageObject()); 309 } 310 311 if (ccr.getResultCode() == ResultCode.SUCCESS) 312 { 313 alertHandlers.put(configuration.dn(), alertHandler); 314 DirectoryServer.registerAlertHandler(alertHandler); 315 } 316 317 return ccr; 318 } 319 320 321 322 /** 323 * Loads the specified class, instantiates it as an alert handler, and 324 * optionally initializes that instance. 325 * 326 * @param className The fully-qualified name of the alert handler class 327 * to load, instantiate, and initialize. 328 * @param configuration The configuration to use to initialize the alert 329 * handler. It must not be {@code null}. 330 * @param initialize Indicates whether the alert handler instance should 331 * be initialized. 332 * 333 * @return The possibly initialized alert handler. 334 * 335 * @throws InitializationException If a problem occurred while attempting to 336 * initialize the alert handler. 337 */ 338 private AlertHandler loadHandler(String className, 339 AlertHandlerCfg configuration, 340 boolean initialize) 341 throws InitializationException 342 { 343 try 344 { 345 AlertHandlerCfgDefn definition = AlertHandlerCfgDefn.getInstance(); 346 ClassPropertyDefinition propertyDefinition = 347 definition.getJavaClassPropertyDefinition(); 348 Class<? extends AlertHandler> handlerClass = 349 propertyDefinition.loadClass(className, AlertHandler.class); 350 AlertHandler handler = handlerClass.newInstance(); 351 352 if (initialize) 353 { 354 handler.initializeAlertHandler(configuration); 355 } 356 else 357 { 358 List<LocalizableMessage> unacceptableReasons = new ArrayList<>(); 359 if (!handler.isConfigurationAcceptable(configuration, unacceptableReasons)) 360 { 361 String reasons = Utils.joinAsString(". ", unacceptableReasons); 362 throw new InitializationException( 363 ERR_CONFIG_ALERTHANDLER_CONFIG_NOT_ACCEPTABLE.get(configuration.dn(), reasons)); 364 } 365 } 366 367 return handler; 368 } 369 catch (Exception e) 370 { 371 LocalizableMessage message = ERR_CONFIG_ALERTHANDLER_INITIALIZATION_FAILED. 372 get(className, configuration.dn(), stackTraceToSingleLineString(e)); 373 throw new InitializationException(message, e); 374 } 375 } 376} 377