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