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