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