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