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