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-2010 Sun Microsystems, Inc.
025 *      Portions Copyright 2013-2015 ForgeRock AS.
026 */
027package org.opends.server.core;
028
029import static org.opends.messages.ConfigMessages.*;
030import static org.opends.server.util.StaticUtils.*;
031
032import java.util.List;
033import java.util.concurrent.ConcurrentHashMap;
034
035import org.forgerock.i18n.LocalizableMessage;
036import org.forgerock.i18n.slf4j.LocalizedLogger;
037import org.forgerock.opendj.config.server.ConfigException;
038import org.forgerock.opendj.ldap.ResultCode;
039import org.opends.server.admin.ClassPropertyDefinition;
040import org.opends.server.admin.server.ConfigurationAddListener;
041import org.opends.server.admin.server.ConfigurationChangeListener;
042import org.opends.server.admin.server.ConfigurationDeleteListener;
043import org.opends.server.admin.server.ServerManagementContext;
044import org.opends.server.admin.std.meta.MonitorProviderCfgDefn;
045import org.opends.server.admin.std.server.MonitorProviderCfg;
046import org.opends.server.admin.std.server.RootCfg;
047import org.opends.server.api.MonitorProvider;
048import org.forgerock.opendj.config.server.ConfigChangeResult;
049import org.opends.server.types.DN;
050import org.opends.server.types.InitializationException;
051
052/**
053 * This class defines a utility that will be used to manage the set of monitor
054 * providers defined in the Directory Server.  It will initialize the monitor
055 * providers when the server starts, and then will manage any additions,
056 * removals, or modifications to any providers while the server is running.
057 */
058public class MonitorConfigManager
059       implements ConfigurationChangeListener<MonitorProviderCfg>,
060                  ConfigurationAddListener<MonitorProviderCfg>,
061                  ConfigurationDeleteListener<MonitorProviderCfg>
062
063{
064
065  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
066
067  /**
068   * A mapping between the DNs of the config entries and the associated monitor
069   * providers.
070   */
071  private final ConcurrentHashMap<DN,MonitorProvider<?>> monitors;
072
073  private final ServerContext serverContext;
074
075  /**
076   * Creates a new instance of this monitor provider config manager.
077   *
078   * @param serverContext
079   *            The server context.
080   */
081  public MonitorConfigManager(ServerContext serverContext)
082  {
083    this.serverContext = serverContext;
084    monitors = new ConcurrentHashMap<>();
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          DirectoryServer.registerMonitorProvider(monitor);
130        }
131        catch (InitializationException ie)
132        {
133          logger.error(ie.getMessageObject());
134          continue;
135        }
136      }
137    }
138  }
139
140
141
142  /** {@inheritDoc} */
143  @Override
144  public boolean isConfigurationAddAcceptable(
145                      MonitorProviderCfg configuration,
146                      List<LocalizableMessage> unacceptableReasons)
147  {
148    if (configuration.isEnabled())
149    {
150      // Get the name of the class and make sure we can instantiate it as a
151      // monitor provider.
152      String className = configuration.getJavaClass();
153      try
154      {
155        loadMonitor(className, null);
156      }
157      catch (InitializationException ie)
158      {
159        unacceptableReasons.add(ie.getMessageObject());
160        return false;
161      }
162    }
163
164    // If we've gotten here, then it's fine.
165    return true;
166  }
167
168
169
170  /** {@inheritDoc} */
171  @Override
172  public ConfigChangeResult applyConfigurationAdd(
173                                 MonitorProviderCfg configuration)
174  {
175    final ConfigChangeResult ccr = new ConfigChangeResult();
176
177    configuration.addChangeListener(this);
178
179    if (! configuration.isEnabled())
180    {
181      return ccr;
182    }
183
184    MonitorProvider<? extends MonitorProviderCfg> monitor = null;
185
186    // Get the name of the class and make sure we can instantiate it as a
187    // monitor provider.
188    String className = configuration.getJavaClass();
189    try
190    {
191      monitor = loadMonitor(className, configuration);
192    }
193    catch (InitializationException ie)
194    {
195      ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode());
196      ccr.addMessage(ie.getMessageObject());
197    }
198
199    if (ccr.getResultCode() == ResultCode.SUCCESS)
200    {
201      monitors.put(configuration.dn(), monitor);
202      DirectoryServer.registerMonitorProvider(monitor);
203    }
204
205    return ccr;
206  }
207
208
209
210  /** {@inheritDoc} */
211  @Override
212  public boolean isConfigurationDeleteAcceptable(
213                      MonitorProviderCfg configuration,
214                      List<LocalizableMessage> unacceptableReasons)
215  {
216    // It will always be acceptable to delete or disable a monitor provider.
217    return true;
218  }
219
220
221
222  /** {@inheritDoc} */
223  @Override
224  public ConfigChangeResult applyConfigurationDelete(
225                                 MonitorProviderCfg configuration)
226  {
227    final ConfigChangeResult ccr = new ConfigChangeResult();
228
229    MonitorProvider<?> monitor = monitors.remove(configuration.dn());
230    if (monitor != null)
231    {
232      DirectoryServer.deregisterMonitorProvider(monitor);
233      monitor.finalizeMonitorProvider();
234    }
235
236    return ccr;
237  }
238
239
240
241  /** {@inheritDoc} */
242  @Override
243  public boolean isConfigurationChangeAcceptable(
244                      MonitorProviderCfg configuration,
245                      List<LocalizableMessage> unacceptableReasons)
246  {
247    if (configuration.isEnabled())
248    {
249      // Get the name of the class and make sure we can instantiate it as a
250      // monitor provider.
251      String className = configuration.getJavaClass();
252      try
253      {
254        loadMonitor(className, null);
255      }
256      catch (InitializationException ie)
257      {
258        unacceptableReasons.add(ie.getMessageObject());
259        return false;
260      }
261    }
262
263    // If we've gotten here, then it's fine.
264    return true;
265  }
266
267
268
269  /** {@inheritDoc} */
270  @Override
271  public ConfigChangeResult applyConfigurationChange(
272                                 MonitorProviderCfg configuration)
273  {
274    final ConfigChangeResult ccr = new ConfigChangeResult();
275
276    // Get the existing monitor provider if it's already enabled.
277    MonitorProvider<?> existingMonitor = monitors.get(configuration.dn());
278
279    // If the new configuration has the monitor disabled, then disable it if it
280    // is enabled, or do nothing if it's already disabled.
281    if (! configuration.isEnabled())
282    {
283      if (existingMonitor != null)
284      {
285        DirectoryServer.deregisterMonitorProvider(existingMonitor);
286        MonitorProvider<?> monitor = monitors.remove(configuration.dn());
287        if (monitor != null)
288        {
289          monitor.finalizeMonitorProvider();
290        }
291      }
292
293      return ccr;
294    }
295
296
297    // Get the class for the monitor provider.  If the monitor is already
298    // enabled, then we shouldn't do anything with it although if the class has
299    // changed then we'll at least need to indicate that administrative action
300    // is required.  If the monitor is disabled, then instantiate the class and
301    // initialize and register it as a monitor provider.
302    String className = configuration.getJavaClass();
303    if (existingMonitor != null)
304    {
305      if (! className.equals(existingMonitor.getClass().getName()))
306      {
307        ccr.setAdminActionRequired(true);
308      }
309
310      return ccr;
311    }
312
313    MonitorProvider<? extends MonitorProviderCfg> monitor = null;
314    try
315    {
316      monitor = loadMonitor(className, configuration);
317    }
318    catch (InitializationException ie)
319    {
320      ccr.setResultCodeIfSuccess(DirectoryServer.getServerErrorResultCode());
321      ccr.addMessage(ie.getMessageObject());
322    }
323
324    if (ccr.getResultCode() == ResultCode.SUCCESS)
325    {
326      monitors.put(configuration.dn(), monitor);
327      DirectoryServer.registerMonitorProvider(monitor);
328    }
329
330    return ccr;
331  }
332
333
334
335  /**
336   * Loads the specified class, instantiates it as a monitor provider, and
337   * optionally initializes that instance.
338   *
339   * @param  className      The fully-qualified name of the monitor provider
340   *                        class to load, instantiate, and initialize.
341   * @param  configuration  The configuration to use to initialize the monitor
342   *                        provider, or {@code null} if the monitor provider
343   *                        should not be initialized.
344   *
345   * @return  The possibly initialized monitor provider.
346   *
347   * @throws  InitializationException  If a problem occurred while attempting to
348   *                                   initialize the monitor provider.
349   */
350  private <T extends MonitorProviderCfg> MonitorProvider<T>
351               loadMonitor(String className, T configuration)
352          throws InitializationException
353  {
354    try
355    {
356      MonitorProviderCfgDefn definition = MonitorProviderCfgDefn.getInstance();
357      ClassPropertyDefinition propertyDefinition =
358           definition.getJavaClassPropertyDefinition();
359      @SuppressWarnings("unchecked")
360      Class<? extends MonitorProvider<T>> providerClass =
361          (Class<? extends MonitorProvider<T>>) propertyDefinition
362              .loadClass(className, MonitorProvider.class);
363      MonitorProvider<T> monitor = providerClass.newInstance();
364
365      if (configuration != null)
366      {
367        monitor.initializeMonitorProvider(configuration);
368      }
369
370      return monitor;
371    }
372    catch (Exception e)
373    {
374      LocalizableMessage message = ERR_CONFIG_MONITOR_INITIALIZATION_FAILED.
375          get(className, configuration.dn(), stackTraceToSingleLineString(e));
376      throw new InitializationException(message, e);
377    }
378  }
379}
380