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-2008 Sun Microsystems, Inc.
015 * Portions Copyright 2014-2016 ForgeRock AS.
016 */
017package org.opends.server.extensions;
018
019import org.forgerock.i18n.LocalizableMessage;
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.List;
023import java.util.concurrent.ConcurrentHashMap;
024import java.util.concurrent.atomic.AtomicLong;
025import javax.management.Attribute;
026import javax.management.AttributeList;
027import javax.management.AttributeNotFoundException;
028import javax.management.DynamicMBean;
029import javax.management.InvalidAttributeValueException;
030import javax.management.MBeanAttributeInfo;
031import javax.management.MBeanConstructorInfo;
032import javax.management.MBeanException;
033import javax.management.MBeanInfo;
034import javax.management.MBeanNotificationInfo;
035import javax.management.MBeanOperationInfo;
036import javax.management.MBeanServer;
037import javax.management.Notification;
038import javax.management.NotificationBroadcasterSupport;
039import javax.management.ObjectName;
040
041import org.opends.server.admin.server.ConfigurationChangeListener;
042import org.opends.server.admin.std.server.AlertHandlerCfg;
043import org.opends.server.admin.std.server.JMXAlertHandlerCfg;
044import org.opends.server.api.AlertGenerator;
045import org.opends.server.api.AlertHandler;
046import org.opends.server.api.DirectoryServerMBean;
047import org.forgerock.opendj.config.server.ConfigException;
048import org.opends.server.config.JMXMBean;
049import org.opends.server.core.DirectoryServer;
050import org.forgerock.i18n.slf4j.LocalizedLogger;
051import org.forgerock.opendj.config.server.ConfigChangeResult;
052import org.forgerock.opendj.ldap.DN;
053import org.opends.server.types.InitializationException;
054import static org.opends.messages.ConfigMessages.*;
055import static org.opends.messages.ExtensionMessages.*;
056import static org.opends.server.util.ServerConstants.*;
057
058/**
059 * This class provides an implementation of a Directory Server alert handler
060 * that will send alerts using JMX notifications.
061 */
062public class JMXAlertHandler
063       extends NotificationBroadcasterSupport
064       implements AlertHandler<JMXAlertHandlerCfg>,
065                  ConfigurationChangeListener<JMXAlertHandlerCfg>, DynamicMBean,
066                  DirectoryServerMBean
067{
068  private static final LocalizedLogger logger = LocalizedLogger.getLoggerForThisClass();
069
070  /** The fully-qualified name of this class. */
071  private static final String CLASS_NAME =
072       "org.opends.server.extensions.JMXAlertHandler";
073
074
075
076  /** The current configuration for this alert handler. */
077  private AlertHandlerCfg currentConfig;
078
079  /** The sequence number generator used for this alert handler. */
080  private AtomicLong sequenceNumber;
081
082  /**
083   * The DN of the configuration entry with which this alert handler is
084   * associated.
085   */
086  private DN configEntryDN;
087
088  /** The JMX object name used for this JMX alert handler. */
089  private ObjectName objectName;
090
091
092
093  /**
094   * Creates a new instance of this JMX alert handler.  No initialization should
095   * be done here, as it should all be performed in the
096   * <CODE>initializeAlertHandler</CODE> method.
097   */
098  public JMXAlertHandler()
099  {
100    super();
101  }
102
103
104
105  /** {@inheritDoc} */
106  public void initializeAlertHandler(JMXAlertHandlerCfg configuration)
107       throws ConfigException, InitializationException
108  {
109    sequenceNumber = new AtomicLong(1);
110
111    if (configuration == null)
112    {
113      configEntryDN = null;
114    }
115    else
116    {
117      configEntryDN = configuration.dn();
118    }
119
120    MBeanServer mBeanServer = DirectoryServer.getJMXMBeanServer();
121    if (mBeanServer != null)
122    {
123      try
124      {
125        String nameStr = MBEAN_BASE_DOMAIN + ":type=JMXAlertHandler";
126        objectName = new ObjectName(nameStr);
127        if (mBeanServer.isRegistered(objectName))
128        {
129          mBeanServer.unregisterMBean(objectName);
130        }
131
132        mBeanServer.registerMBean(this, objectName);
133      }
134      catch (Exception e)
135      {
136        logger.traceException(e);
137
138        LocalizableMessage message = ERR_JMX_ALERT_HANDLER_CANNOT_REGISTER.get(e);
139        throw new InitializationException(message, e);
140      }
141    }
142
143    if (configuration != null)
144    {
145      configuration.addJMXChangeListener(this);
146      currentConfig = configuration;
147    }
148  }
149
150
151
152  /** {@inheritDoc} */
153  public AlertHandlerCfg getAlertHandlerConfiguration()
154  {
155    return currentConfig;
156  }
157
158
159
160  /** {@inheritDoc} */
161  public boolean isConfigurationAcceptable(AlertHandlerCfg configuration,
162                                           List<LocalizableMessage> unacceptableReasons)
163  {
164    JMXAlertHandlerCfg cfg = (JMXAlertHandlerCfg) configuration;
165    return isConfigurationChangeAcceptable(cfg, unacceptableReasons);
166  }
167
168
169
170  /** {@inheritDoc} */
171  public void finalizeAlertHandler()
172  {
173    // No action is required.
174  }
175
176
177
178  /**
179   * Retrieves the JMX object name for this JMX alert handler.
180   *
181   * @return  The JMX object name for this JMX alert handler.
182   */
183  public ObjectName getObjectName()
184  {
185    return objectName;
186  }
187
188
189
190  /** {@inheritDoc} */
191  public void sendAlertNotification(AlertGenerator generator, String alertType,
192                                    LocalizableMessage alertMessage)
193  {
194    sendNotification(new Notification(alertType, generator.getClassName(),
195                                      sequenceNumber.getAndIncrement(),
196                                      System.currentTimeMillis(),
197                                      alertMessage.toString()));
198  }
199
200
201
202  /**
203   * Retrieves information about the types of JMX notifications that may be
204   * generated.
205   *
206   * @return  Information about the types of JMX notifications that may be
207   *          generated.
208   */
209  public MBeanNotificationInfo[] getNotificationInfo()
210  {
211    ArrayList<MBeanNotificationInfo> notifications = new ArrayList<>();
212    ConcurrentHashMap<DN,JMXMBean> mBeans = DirectoryServer.getJMXMBeans();
213    for (JMXMBean mBean : mBeans.values())
214    {
215      MBeanInfo mBeanInfo = mBean.getMBeanInfo();
216      Collections.addAll(notifications, mBeanInfo.getNotifications());
217    }
218
219    return notifications.toArray(new MBeanNotificationInfo[notifications.size()]);
220  }
221
222
223
224  /**
225   * Obtain the value of a specific attribute of the Dynamic MBean.
226   *
227   * @param  attribute  The name of the attribute to be retrieved.
228   *
229   * @return  The requested MBean attribute.
230   *
231   * @throws  AttributeNotFoundException  If the specified attribute is not
232   *                                      associated with this MBean.
233   */
234  public Attribute getAttribute(String attribute)
235         throws AttributeNotFoundException
236  {
237    // There are no attributes for this MBean.
238    LocalizableMessage message = ERR_CONFIG_JMX_ATTR_NO_ATTR.get(configEntryDN, attribute);
239    throw new AttributeNotFoundException(message.toString());
240  }
241
242
243
244  /**
245   * Set the value of a specific attribute of the Dynamic MBean.
246   *
247   * @param  attribute  The identification of the attribute to be set and the
248   *                    value it is to be set to.
249   *
250   * @throws  AttributeNotFoundException  If the specified attribute is not
251   *                                       associated with this MBean.
252   *
253   * @throws  InvalidAttributeValueException  If the provided value is not
254   *                                          acceptable for this MBean.
255   */
256  public void setAttribute(Attribute attribute)
257         throws AttributeNotFoundException, InvalidAttributeValueException
258  {
259    // There are no attributes for this MBean.
260    LocalizableMessage message = ERR_CONFIG_JMX_ATTR_NO_ATTR.get(configEntryDN, attribute);
261    throw new AttributeNotFoundException(message.toString());
262  }
263
264
265
266  /**
267   * Get the values of several attributes of the Dynamic MBean.
268   *
269   * @param  attributes  A list of the attributes to be retrieved.
270   *
271   * @return  The list of attributes retrieved.
272   */
273  public AttributeList getAttributes(String[] attributes)
274  {
275    // There are no attributes for this MBean.
276    return new AttributeList();
277  }
278
279
280
281  /**
282   * Sets the values of several attributes of the Dynamic MBean.
283   *
284   * @param  attributes  A list of attributes:  The identification of the
285   *                     attributes to be set and the values they are to be set
286   *                     to.
287   *
288   * @return  The list of attributes that were set with their new values.
289   */
290  public AttributeList setAttributes(AttributeList attributes)
291  {
292    // There are no attributes for this MBean.
293    return new AttributeList();
294  }
295
296
297
298  /**
299   * Allows an action to be invoked on the Dynamic MBean.
300   *
301   * @param  actionName  The name of the action to be invoked.
302   * @param  params      An array containing the parameters to be set when the
303   *                     action is invoked.
304   * @param  signature   An array containing the signature of the action.  The
305   *                     class objects will be loaded through the same class
306   *                     loader as the one used for loading the MBean on which
307   *                     action is invoked.
308   *
309   * @return  The object returned by the action, which represents the result of
310   *          invoking the action on the MBean specified.
311   *
312   * @throws  MBeanException  If a problem is encountered while invoking the
313   *                          method.
314   */
315  public Object invoke(String actionName, Object[] params, String[] signature)
316         throws MBeanException
317  {
318    // There are no invokable components for this MBean.
319    StringBuilder buffer = new StringBuilder();
320    buffer.append(actionName);
321    buffer.append("(");
322
323    if (signature.length > 0)
324    {
325      buffer.append(signature[0]);
326
327      for (int i=1; i < signature.length; i++)
328      {
329        buffer.append(", ");
330        buffer.append(signature[i]);
331      }
332    }
333
334    buffer.append(")");
335
336    LocalizableMessage message = ERR_CONFIG_JMX_NO_METHOD.get(buffer, configEntryDN);
337    throw new MBeanException(new ConfigException(message));
338  }
339
340
341
342  /**
343   * Provides the exposed attributes and actions of the Dynamic MBean using an
344   * MBeanInfo object.
345   *
346   * @return  An instance of <CODE>MBeanInfo</CODE> allowing all attributes and
347   *          actions exposed by this Dynamic MBean to be retrieved.
348   */
349  public MBeanInfo getMBeanInfo()
350  {
351    return new MBeanInfo(CLASS_NAME, "JMX Alert Handler",
352                         new MBeanAttributeInfo[0], new MBeanConstructorInfo[0],
353                         new MBeanOperationInfo[0], getNotificationInfo());
354  }
355
356
357
358  /** {@inheritDoc} */
359  public boolean isConfigurationChangeAcceptable(
360                      JMXAlertHandlerCfg configuration,
361                      List<LocalizableMessage> unacceptableReasons)
362  {
363    return true;
364  }
365
366
367
368  /** {@inheritDoc} */
369  public ConfigChangeResult applyConfigurationChange(
370                                        JMXAlertHandlerCfg configuration)
371  {
372    currentConfig = configuration;
373    return new ConfigChangeResult();
374  }
375}