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