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