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