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