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