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