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