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}