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