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
010     * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011     * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012     * See the License for the specific language governing permissions
013     * and limitations under the License.
014     *
015     * When distributing Covered Code, include this CDDL HEADER in each
016     * file and include the License file at
017     * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
018     * add the following below this CDDL HEADER, with the fields enclosed
019     * by brackets "[]" replaced with your own identifying information:
020     *      Portions Copyright [yyyy] [name of copyright owner]
021     *
022     * CDDL HEADER END
023     *
024     *
025     *      Copyright 2006-2008 Sun Microsystems, Inc.
026     */
027    package org.opends.server.core;
028    import org.opends.messages.Message;
029    
030    
031    
032    import java.lang.reflect.Method;
033    import java.util.ArrayList;
034    import java.util.Iterator;
035    import java.util.List;
036    import java.util.concurrent.ConcurrentHashMap;
037    
038    import org.opends.server.admin.ClassPropertyDefinition;
039    import org.opends.server.admin.server.ConfigurationAddListener;
040    import org.opends.server.admin.server.ConfigurationChangeListener;
041    import org.opends.server.admin.server.ConfigurationDeleteListener;
042    import org.opends.server.admin.std.meta.AlertHandlerCfgDefn;
043    import org.opends.server.admin.std.server.AlertHandlerCfg;
044    import org.opends.server.admin.std.server.RootCfg;
045    import org.opends.server.admin.server.ServerManagementContext;
046    import org.opends.server.api.AlertHandler;
047    import org.opends.server.config.ConfigException;
048    import org.opends.server.types.ConfigChangeResult;
049    import org.opends.server.types.DN;
050    
051    
052    import org.opends.server.types.InitializationException;
053    import org.opends.server.types.ResultCode;
054    
055    import static org.opends.messages.ConfigMessages.*;
056    
057    import static org.opends.server.util.StaticUtils.*;
058    import org.opends.server.loggers.ErrorLogger;
059    
060    
061    /**
062     * This class defines a utility that will be used to manage the set of alert
063     * handlers defined in the Directory Server.  It will initialize the alert
064     * handlers when the server starts, and then will manage any additions,
065     * removals, or modifications to any alert handlers while the server is running.
066     */
067    public class AlertHandlerConfigManager
068           implements ConfigurationChangeListener<AlertHandlerCfg>,
069                      ConfigurationAddListener<AlertHandlerCfg>,
070                      ConfigurationDeleteListener<AlertHandlerCfg>
071    
072    {
073      // A mapping between the DNs of the config entries and the associated alert
074      // handlers.
075      private ConcurrentHashMap<DN,AlertHandler> alertHandlers;
076    
077    
078    
079      /**
080       * Creates a new instance of this alert handler config manager.
081       */
082      public AlertHandlerConfigManager()
083      {
084        alertHandlers = new ConcurrentHashMap<DN,AlertHandler>();
085      }
086    
087    
088    
089      /**
090       * Initializes all alert handlers currently defined in the Directory Server
091       * configuration.  This should only be called at Directory Server startup.
092       *
093       * @throws  ConfigException  If a configuration problem causes the alert
094       *                           handler initialization process to fail.
095       *
096       * @throws  InitializationException  If a problem occurs while initializing
097       *                                   the alert handlers that is not related to
098       *                                   the server configuration.
099       */
100      public void initializeAlertHandlers()
101             throws ConfigException, InitializationException
102      {
103        // Get the root configuration object.
104        ServerManagementContext managementContext =
105             ServerManagementContext.getInstance();
106        RootCfg rootConfiguration = managementContext.getRootConfiguration();
107    
108    
109        // Register as an add and delete listener with the root configuration so we
110        // can be notified if any alert handler entries are added or removed.
111        rootConfiguration.addAlertHandlerAddListener(this);
112        rootConfiguration.addAlertHandlerDeleteListener(this);
113    
114    
115        //Initialize the existing alert handlers.
116        for (String name : rootConfiguration.listAlertHandlers())
117        {
118          AlertHandlerCfg configuration = rootConfiguration.getAlertHandler(name);
119          configuration.addChangeListener(this);
120    
121          if (configuration.isEnabled())
122          {
123            String className = configuration.getJavaClass();
124            try
125            {
126              AlertHandler handler = loadHandler(className, configuration, true);
127              alertHandlers.put(configuration.dn(), handler);
128              DirectoryServer.registerAlertHandler(handler);
129            }
130            catch (InitializationException ie)
131            {
132              ErrorLogger.logError(ie.getMessageObject());
133              continue;
134            }
135          }
136        }
137      }
138    
139    
140    
141      /**
142       * {@inheritDoc}
143       */
144      public boolean isConfigurationAddAcceptable(AlertHandlerCfg configuration,
145                                                  List<Message> unacceptableReasons)
146      {
147        if (configuration.isEnabled())
148        {
149          // Get the name of the class and make sure we can instantiate it as an
150          // alert handler.
151          String className = configuration.getJavaClass();
152          try
153          {
154            loadHandler(className, configuration, false);
155          }
156          catch (InitializationException ie)
157          {
158            unacceptableReasons.add(ie.getMessageObject());
159            return false;
160          }
161        }
162    
163        // If we've gotten here, then it's fine.
164        return true;
165      }
166    
167    
168    
169      /**
170       * {@inheritDoc}
171       */
172      public ConfigChangeResult applyConfigurationAdd(AlertHandlerCfg configuration)
173      {
174        ResultCode         resultCode          = ResultCode.SUCCESS;
175        boolean            adminActionRequired = false;
176        ArrayList<Message> messages            = new ArrayList<Message>();
177    
178        configuration.addChangeListener(this);
179    
180        if (! configuration.isEnabled())
181        {
182          return new ConfigChangeResult(resultCode, adminActionRequired, messages);
183        }
184    
185        AlertHandler alertHandler = null;
186    
187        // Get the name of the class and make sure we can instantiate it as an alert
188        // handler.
189        String className = configuration.getJavaClass();
190        try
191        {
192          alertHandler = loadHandler(className, configuration, true);
193        }
194        catch (InitializationException ie)
195        {
196          if (resultCode == ResultCode.SUCCESS)
197          {
198            resultCode = DirectoryServer.getServerErrorResultCode();
199          }
200    
201          messages.add(ie.getMessageObject());
202        }
203    
204        if (resultCode == ResultCode.SUCCESS)
205        {
206          alertHandlers.put(configuration.dn(), alertHandler);
207          DirectoryServer.registerAlertHandler(alertHandler);
208        }
209    
210        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
211      }
212    
213    
214    
215      /**
216       * {@inheritDoc}
217       */
218      public boolean isConfigurationDeleteAcceptable(
219                          AlertHandlerCfg configuration,
220                          List<Message> unacceptableReasons)
221      {
222        // FIXME -- We should try to perform some check to determine whether the
223        // alert handler is in use.
224        return true;
225      }
226    
227    
228    
229      /**
230       * {@inheritDoc}
231       */
232      public ConfigChangeResult applyConfigurationDelete(
233                                     AlertHandlerCfg configuration)
234      {
235        ResultCode         resultCode          = ResultCode.SUCCESS;
236        boolean            adminActionRequired = false;
237        ArrayList<Message> messages            = new ArrayList<Message>();
238    
239        AlertHandler alertHandler = alertHandlers.remove(configuration.dn());
240        if (alertHandler != null)
241        {
242          DirectoryServer.deregisterAlertHandler(alertHandler);
243          alertHandler.finalizeAlertHandler();
244        }
245    
246        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
247      }
248    
249    
250    
251      /**
252       * {@inheritDoc}
253       */
254      public boolean isConfigurationChangeAcceptable(AlertHandlerCfg configuration,
255                          List<Message> unacceptableReasons)
256      {
257        if (configuration.isEnabled())
258        {
259          // Get the name of the class and make sure we can instantiate it as an
260          // alert handler.
261          String className = configuration.getJavaClass();
262          try
263          {
264            loadHandler(className, configuration, false);
265          }
266          catch (InitializationException ie)
267          {
268            unacceptableReasons.add(ie.getMessageObject());
269            return false;
270          }
271        }
272    
273        // If we've gotten here, then it's fine.
274        return true;
275      }
276    
277    
278    
279      /**
280       * {@inheritDoc}
281       */
282      public ConfigChangeResult applyConfigurationChange(
283                                     AlertHandlerCfg configuration)
284      {
285        ResultCode        resultCode          = ResultCode.SUCCESS;
286        boolean           adminActionRequired = false;
287        ArrayList<Message> messages            = new ArrayList<Message>();
288    
289    
290        // Get the existing alert handler if it's already enabled.
291        AlertHandler existingHandler = alertHandlers.get(configuration.dn());
292    
293    
294        // If the new configuration has the handler disabled, then disable it if it
295        // is enabled, or do nothing if it's already disabled.
296        if (! configuration.isEnabled())
297        {
298          if (existingHandler != null)
299          {
300            DirectoryServer.deregisterAlertHandler(existingHandler);
301    
302            AlertHandler alertHandler = alertHandlers.remove(configuration.dn());
303            if (alertHandler != null)
304            {
305              alertHandler.finalizeAlertHandler();
306            }
307          }
308    
309          return new ConfigChangeResult(resultCode, adminActionRequired, messages);
310        }
311    
312    
313        // Get the class for the alert handler.  If the handler is already enabled,
314        // then we shouldn't do anything with it although if the class has changed
315        // then we'll at least need to indicate that administrative action is
316        // required.  If the handler is disabled, then instantiate the class and
317        // initialize and register it as an alert handler.
318        String className = configuration.getJavaClass();
319        if (existingHandler != null)
320        {
321          if (! className.equals(existingHandler.getClass().getName()))
322          {
323            adminActionRequired = true;
324          }
325    
326          return new ConfigChangeResult(resultCode, adminActionRequired, messages);
327        }
328    
329        AlertHandler alertHandler = null;
330        try
331        {
332          alertHandler = loadHandler(className, configuration, true);
333        }
334        catch (InitializationException ie)
335        {
336          if (resultCode == ResultCode.SUCCESS)
337          {
338            resultCode = DirectoryServer.getServerErrorResultCode();
339          }
340    
341          messages.add(ie.getMessageObject());
342        }
343    
344        if (resultCode == ResultCode.SUCCESS)
345        {
346          alertHandlers.put(configuration.dn(), alertHandler);
347          DirectoryServer.registerAlertHandler(alertHandler);
348        }
349    
350        return new ConfigChangeResult(resultCode, adminActionRequired, messages);
351      }
352    
353    
354    
355      /**
356       * Loads the specified class, instantiates it as an alert handler, and
357       * optionally initializes that instance.
358       *
359       * @param  className      The fully-qualified name of the alert handler class
360       *                        to load, instantiate, and initialize.
361       * @param  configuration  The configuration to use to initialize the alert
362       *                        handler.  It must not be {@code null}.
363       * @param  initialize     Indicates whether the alert handler instance should
364       *                        be initialized.
365       *
366       * @return  The possibly initialized alert handler.
367       *
368       * @throws  InitializationException  If a problem occurred while attempting to
369       *                                   initialize the alert handler.
370       */
371      private AlertHandler loadHandler(String className,
372                                       AlertHandlerCfg configuration,
373                                       boolean initialize)
374              throws InitializationException
375      {
376        try
377        {
378          AlertHandlerCfgDefn definition = AlertHandlerCfgDefn.getInstance();
379          ClassPropertyDefinition propertyDefinition =
380               definition.getJavaClassPropertyDefinition();
381          Class<? extends AlertHandler> handlerClass =
382               propertyDefinition.loadClass(className, AlertHandler.class);
383          AlertHandler handler = handlerClass.newInstance();
384    
385          if (initialize)
386          {
387            Method method = handler.getClass().getMethod("initializeAlertHandler",
388                configuration.configurationClass());
389            method.invoke(handler, configuration);
390          }
391          else
392          {
393            Method method =
394                 handler.getClass().getMethod("isConfigurationAcceptable",
395                                              AlertHandlerCfg.class, List.class);
396    
397            List<Message> unacceptableReasons = new ArrayList<Message>();
398            Boolean acceptable = (Boolean) method.invoke(handler, configuration,
399                                                         unacceptableReasons);
400            if (! acceptable)
401            {
402              StringBuilder buffer = new StringBuilder();
403              if (! unacceptableReasons.isEmpty())
404              {
405                Iterator<Message> iterator = unacceptableReasons.iterator();
406                buffer.append(iterator.next());
407                while (iterator.hasNext())
408                {
409                  buffer.append(".  ");
410                  buffer.append(iterator.next());
411                }
412              }
413    
414              Message message = ERR_CONFIG_ALERTHANDLER_CONFIG_NOT_ACCEPTABLE.get(
415                  String.valueOf(configuration.dn()), buffer.toString());
416              throw new InitializationException(message);
417            }
418          }
419    
420          return handler;
421        }
422        catch (Exception e)
423        {
424          Message message = ERR_CONFIG_ALERTHANDLER_INITIALIZATION_FAILED.
425              get(className, String.valueOf(configuration.dn()),
426                  stackTraceToSingleLineString(e));
427          throw new InitializationException(message, e);
428        }
429      }
430    }
431