/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at * trunk/opends/resource/legal-notices/OpenDS.LICENSE * or https://OpenDS.dev.java.net/OpenDS.LICENSE. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable, * add the following below this CDDL HEADER, with the fields enclosed * by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * * Portions Copyright 2006-2007-2008 Sun Microsystems, Inc. */ package org.opends.server.config; import org.opends.messages.Message; import java.util.ArrayList; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.AttributeNotFoundException; import javax.management.DynamicMBean; import javax.management.MBeanAttributeInfo; import javax.management.InvalidAttributeValueException; import javax.management.MBeanConstructorInfo; import javax.management.MBeanException; import javax.management.MBeanInfo; import javax.management.MBeanNotificationInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanServer; import javax.management.ObjectName; import org.opends.server.admin.std.server.MonitorProviderCfg; import org.opends.server.api.AlertGenerator; import org.opends.server.api.ClientConnection; import org.opends.server.api.DirectoryServerMBean; import org.opends.server.api.InvokableComponent; import org.opends.server.api.MonitorProvider; import org.opends.server.core.DirectoryServer; import org.opends.server.protocols.jmx.Credential; import static org.opends.server.loggers.debug.DebugLogger.*; import static org.opends.server.loggers.ErrorLogger.*; import org.opends.server.loggers.debug.DebugTracer; import static org.opends.messages.ConfigMessages.*; import static org.opends.server.util.ServerConstants.*; import static org.opends.server.util.StaticUtils.*; import org.opends.server.protocols.jmx.JmxClientConnection; import org.opends.server.protocols.internal.InternalClientConnection; import org.opends.server.protocols.ldap.LDAPFilter; import org.opends.server.protocols.internal.InternalSearchOperation ; import org.opends.server.types.*; /** * This class defines a JMX MBean that can be registered with the Directory * Server to provide monitoring and statistical information, provide read and/or * read-write access to the configuration, and provide notifications and alerts * if a significant event or severe/fatal error occurs. */ @org.opends.server.types.PublicAPI( stability=org.opends.server.types.StabilityLevel.VOLATILE, mayInstantiate=true, mayExtend=false, mayInvoke=true) public final class JMXMBean implements DynamicMBean, DirectoryServerMBean { /** * The tracer object for the debug logger. */ private static final DebugTracer TRACER = getTracer(); /** * The fully-qualified name of this class. */ private static final String CLASS_NAME = "org.opends.server.config.JMXMBean"; // The set of alert generators for this MBean. private CopyOnWriteArrayList<AlertGenerator> alertGenerators; // The set of invokable components for this MBean. private CopyOnWriteArrayList<InvokableComponent> invokableComponents; // The set of monitor providers for this MBean. private CopyOnWriteArrayList<MonitorProvider<? extends MonitorProviderCfg>> monitorProviders; // The DN of the configuration entry with which this MBean is associated. private DN configEntryDN; // The object name for this MBean. private ObjectName objectName; /** * Creates a JMX object name string based on a DN. * * @param configEntryDN The DN of the configuration entry with which * this ObjectName is associated. * * @return The string representation of the JMX Object Name * associated with the input DN. */ public static String getJmxName (DN configEntryDN) { String typeStr = null; String nameStr = null ; try { String dnString = configEntryDN.toString(); if ( ! ((dnString == null) || (dnString.length() == 0))) { StringBuilder buffer = new StringBuilder(dnString.length()); String rdns[] = dnString.replace(',', ';').split(";"); for (int j = rdns.length - 1; j >= 0; j--) { int rdnIndex = rdns.length - j; buffer.append(",Rdn" + rdnIndex + "=") ; for (int i = 0; i < rdns[j].length(); i++) { char c = rdns[j].charAt(i); if (isAlpha(c) || isDigit(c)) { buffer.append(c); } else { switch (c) { case ' ': buffer.append("_"); break; case '=': buffer.append("-"); } } } } typeStr = buffer.toString(); } nameStr = MBEAN_BASE_DOMAIN + ":" + "Name=rootDSE" + typeStr; } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_CONFIG_JMX_CANNOT_REGISTER_MBEAN.get( configEntryDN.toString(), String.valueOf(e)); logError(message); } return nameStr ; } /** * Creates a new dynamic JMX MBean for use with the Directory Server. * * @param configEntryDN The DN of the configuration entry with which this * MBean is associated. */ public JMXMBean(DN configEntryDN) { this.configEntryDN = configEntryDN; alertGenerators = new CopyOnWriteArrayList<AlertGenerator>(); invokableComponents = new CopyOnWriteArrayList<InvokableComponent>(); monitorProviders = new CopyOnWriteArrayList<MonitorProvider< ? extends MonitorProviderCfg>>(); MBeanServer mBeanServer = DirectoryServer.getJMXMBeanServer(); if (mBeanServer != null) { try { objectName = new ObjectName(getJmxName(configEntryDN)) ; try { if(mBeanServer.isRegistered(objectName)) { mBeanServer.unregisterMBean(objectName); } } catch(Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } } mBeanServer.registerMBean(this, objectName); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } e.printStackTrace(); Message message = ERR_CONFIG_JMX_CANNOT_REGISTER_MBEAN.get( configEntryDN.toString(), String.valueOf(e)); logError(message); } } } /** * Retrieves the JMX object name for this JMX MBean. * * @return The JMX object name for this JMX MBean. */ public ObjectName getObjectName() { return objectName; } /** * Retrieves the set of alert generators for this JMX MBean. * * @return The set of alert generators for this JMX MBean. */ public CopyOnWriteArrayList<AlertGenerator> getAlertGenerators() { return alertGenerators; } /** * Adds the provided alert generator to the set of alert generators associated * with this JMX MBean. * * @param generator The alert generator to add to the set of alert * generators for this JMX MBean. */ public void addAlertGenerator(AlertGenerator generator) { synchronized (alertGenerators) { if (! alertGenerators.contains(generator)) { alertGenerators.add(generator); } } } /** * Removes the provided alert generator from the set of alert generators * associated with this JMX MBean. * * @param generator The alert generator to remove from the set of alert * generators for this JMX MBean. * * @return <CODE>true</CODE> if the alert generator was removed, or * <CODE>false</CODE> if it was not associated with this MBean. */ public boolean removeAlertGenerator(AlertGenerator generator) { synchronized (alertGenerators) { return alertGenerators.remove(generator); } } /** * Retrieves the set of invokable components associated with this JMX MBean. * * @return The set of invokable components associated with this JMX MBean. */ public CopyOnWriteArrayList<InvokableComponent> getInvokableComponents() { return invokableComponents; } /** * Adds the provided invokable component to the set of components associated * with this JMX MBean. * * @param component The component to add to the set of invokable components * for this JMX MBean. */ public void addInvokableComponent(InvokableComponent component) { synchronized (invokableComponents) { if (! invokableComponents.contains(component)) { invokableComponents.add(component); } } } /** * Removes the provided invokable component from the set of components * associated with this JMX MBean. * * @param component The component to remove from the set of invokable * components for this JMX MBean. * * @return <CODE>true</CODE> if the specified component was successfully * removed, or <CODE>false</CODE> if not. */ public boolean removeInvokableComponent(InvokableComponent component) { synchronized (invokableComponents) { return invokableComponents.remove(component); } } /** * Retrieves the set of monitor providers associated with this JMX MBean. * * @return The set of monitor providers associated with this JMX MBean. */ public CopyOnWriteArrayList<MonitorProvider<? extends MonitorProviderCfg>> getMonitorProviders() { return monitorProviders; } /** * Adds the given monitor provider to the set of components associated with * this JMX MBean. * * @param component The component to add to the set of monitor providers * for this JMX MBean. */ public void addMonitorProvider(MonitorProvider<? extends MonitorProviderCfg> component) { synchronized (monitorProviders) { if (! monitorProviders.contains(component)) { monitorProviders.add(component); } } } /** * Removes the given monitor provider from the set of components associated * with this JMX MBean. * * @param component The component to remove from the set of monitor * providers for this JMX MBean. * * @return <CODE>true</CODE> if the specified component was successfully * removed, or <CODE>false</CODE> if not. */ public boolean removeMonitorProvider(MonitorProvider<?> component) { synchronized (monitorProviders) { return monitorProviders.remove(component); } } /** * Retrieves the specified configuration attribute. * * @param name The name of the configuration attribute to retrieve. * * @return The specified configuration attribute, or <CODE>null</CODE> if * there is no such attribute. */ private Attribute getJmxAttribute(String name) { // It's possible that this is a monitor attribute rather than a configurable // one. Check all of those. AttributeType attrType = DirectoryServer.getAttributeType(name.toLowerCase()); if (attrType == null) { attrType = DirectoryServer.getDefaultAttributeType(name); } for (MonitorProvider<? extends MonitorProviderCfg> monitor : monitorProviders) { for (org.opends.server.types.Attribute a : monitor.getMonitorData()) { if (attrType.equals(a.getAttributeType())) { if (a.isEmpty()) { continue; } Iterator<AttributeValue> iterator = a.iterator(); AttributeValue value = iterator.next(); if (iterator.hasNext()) { ArrayList<String> stringValues = new ArrayList<String>(); stringValues.add(value.getValue().toString()); while (iterator.hasNext()) { value = iterator.next(); stringValues.add(value.getValue().toString()); } String[] valueArray = new String[stringValues.size()]; stringValues.toArray(valueArray); return new Attribute(name, valueArray); } else { return new Attribute(name, value.getValue().toString()); } } } } return null; } /** * Obtain the value of a specific attribute of the Dynamic MBean. * * @param attributeName The name of the attribute to be retrieved. * * @return The requested attribute. * * @throws AttributeNotFoundException If the specified attribute is not * associated with this MBean. */ public Attribute getAttribute(String attributeName) throws AttributeNotFoundException { // // Get the jmx Client connection ClientConnection clientConnection = getClientConnection(); if (clientConnection == null) { return null; } // // prepare the ldap search LDAPFilter filter; try { filter = LDAPFilter.decode("objectclass=*"); } catch (LDAPException e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_CONFIG_JMX_CANNOT_GET_ATTRIBUTE. get(String.valueOf(attributeName), String.valueOf(configEntryDN), getExceptionMessage(e)); throw new AttributeNotFoundException(message.toString()); } // // Perform the Ldap operation for // - ACI Check // - Loggin purpose InternalSearchOperation op=null; if (clientConnection instanceof JmxClientConnection) { op = ((JmxClientConnection)clientConnection).processSearch( ByteString.valueOf(configEntryDN.toString()), SearchScope.BASE_OBJECT, filter); } else if (clientConnection instanceof InternalClientConnection) { op = ((InternalClientConnection)clientConnection).processSearch( ByteString.valueOf(configEntryDN.toString()), SearchScope.BASE_OBJECT, filter); } // BUG : op may be null ResultCode rc = op.getResultCode(); if (rc != ResultCode.SUCCESS) { clientConnection = null ; Message message = ERR_CONFIG_JMX_CANNOT_GET_ATTRIBUTE. get(String.valueOf(attributeName), String.valueOf(configEntryDN), String.valueOf(op.getErrorMessage())); throw new AttributeNotFoundException(message.toString()); } try { return getJmxAttribute(attributeName); } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } Message message = ERR_CONFIG_JMX_ATTR_NO_ATTR.get( String.valueOf(configEntryDN), attributeName); logError(message); throw new AttributeNotFoundException(message.toString()); } } /** * Set the value of a specific attribute of the Dynamic MBean. In this case, * it will always throw {@code InvalidAttributeValueException} because setting * attribute values over JMX is currently not allowed. * * @param attribute The identification of the attribute to be set and the * value it is to be set to. * * @throws AttributeNotFoundException If the specified attribute is not * associated with this MBean. * * @throws InvalidAttributeValueException If the provided value is not * acceptable for this MBean. */ public void setAttribute(javax.management.Attribute attribute) throws AttributeNotFoundException, InvalidAttributeValueException { throw new InvalidAttributeValueException(); } /** * Get the values of several attributes of the Dynamic MBean. * * @param attributes A list of the attributes to be retrieved. * * @return The list of attributes retrieved. */ public AttributeList getAttributes(String[] attributes) { // // Get the jmx Client connection ClientConnection clientConnection = getClientConnection(); if (clientConnection == null) { return null; } // // prepare the ldap search LDAPFilter filter; try { filter = LDAPFilter.decode("objectclass=*"); } catch (LDAPException e) { return null; } // // Perform the Ldap operation for // - ACI Check // - Loggin purpose InternalSearchOperation op=null; if (clientConnection instanceof JmxClientConnection) { op = ((JmxClientConnection)clientConnection).processSearch( ByteString.valueOf(configEntryDN.toString()), SearchScope.BASE_OBJECT, filter); } else if (clientConnection instanceof InternalClientConnection) { op = ((InternalClientConnection)clientConnection).processSearch( ByteString.valueOf(configEntryDN.toString()), SearchScope.BASE_OBJECT, filter); } // BUG: op may be null ResultCode rc = op.getResultCode(); if (rc != ResultCode.SUCCESS) { clientConnection = null ; return null; } AttributeList attrList = new AttributeList(attributes.length); Attribute attr; for (String name : attributes) { try { if ((attr = getJmxAttribute(name)) != null) { attrList.add(attr); continue; } } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } } // It's possible that this is a monitor attribute rather than a // configurable one. Check all of those. AttributeType attrType = DirectoryServer.getAttributeType(name.toLowerCase()); if (attrType == null) { attrType = DirectoryServer.getDefaultAttributeType(name); } monitorLoop: for (MonitorProvider<? extends MonitorProviderCfg> monitor : monitorProviders) { for (org.opends.server.types.Attribute a : monitor.getMonitorData()) { if (attrType.equals(a.getAttributeType())) { if (a.isEmpty()) { continue; } Iterator<AttributeValue> iterator = a.iterator(); AttributeValue value = iterator.next(); if (iterator.hasNext()) { ArrayList<String> stringValues = new ArrayList<String>(); stringValues.add(value.getValue().toString()); while (iterator.hasNext()) { value = iterator.next(); stringValues.add(value.getValue().toString()); } String[] valueArray = new String[stringValues.size()]; stringValues.toArray(valueArray); attrList.add(new Attribute(name, valueArray)); break monitorLoop; } else { attrList.add(new Attribute(name, value.getValue().toString())); break monitorLoop; } } } } } return attrList; } /** * Sets the values of several attributes of the Dynamic MBean. * * @param attributes A list of attributes: The identification of the * attributes to be set and the values they are to be set * to. * * @return The list of attributes that were set with their new values. In * this case, the list will always be empty because we do not support * setting attribute values over JMX. */ public AttributeList setAttributes(AttributeList attributes) { return new AttributeList(); } /** * Allows an action to be invoked on the Dynamic MBean. * * @param actionName The name of the action to be invoked. * @param params An array containing the parameters to be set when the * action is invoked. * @param signature An array containing the signature of the action. The * class objects will be loaded through the same class * loader as the one used for loading the MBean on which * action is invoked. * * @return The object returned by the action, which represents the result of * invoking the action on the MBean specified. * * @throws MBeanException If a problem is encountered while invoking the * method. */ public Object invoke(String actionName, Object[] params, String[] signature) throws MBeanException { for (InvokableComponent component : invokableComponents) { for (InvokableMethod method : component.getOperationSignatures()) { if (method.hasSignature(actionName, signature)) { try { method.invoke(component, params); } catch (MBeanException me) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, me); } throw me; } catch (Exception e) { if (debugEnabled()) { TRACER.debugCaught(DebugLogLevel.ERROR, e); } throw new MBeanException(e); } } } } // If we've gotten here, then there is no such method so throw an exception. StringBuilder buffer = new StringBuilder(); buffer.append(actionName); buffer.append("("); if (signature.length > 0) { buffer.append(signature[0]); for (int i=1; i < signature.length; i++) { buffer.append(", "); buffer.append(signature[i]); } } buffer.append(")"); Message message = ERR_CONFIG_JMX_NO_METHOD.get( buffer.toString(), configEntryDN.toString()); throw new MBeanException( new DirectoryException(ResultCode.NO_SUCH_OPERATION, message)); } /** * Provides the exposed attributes and actions of the Dynamic MBean using an * MBeanInfo object. * * @return An instance of <CODE>MBeanInfo</CODE> allowing all attributes and * actions exposed by this Dynamic MBean to be retrieved. */ public MBeanInfo getMBeanInfo() { ClientConnection clientConnection = getClientConnection(); if (clientConnection == null) { return new MBeanInfo(CLASS_NAME, null, null, null, null, null); } ArrayList<MBeanAttributeInfo> attrs = new ArrayList<MBeanAttributeInfo>(); for (MonitorProvider<? extends MonitorProviderCfg> monitor : monitorProviders) { for (org.opends.server.types.Attribute a : monitor.getMonitorData()) { attrs.add(new MBeanAttributeInfo(a.getName(), String.class.getName(), null, true, false, false)); } } MBeanAttributeInfo[] mBeanAttributes = new MBeanAttributeInfo[attrs.size()]; attrs.toArray(mBeanAttributes); ArrayList<MBeanNotificationInfo> notifications = new ArrayList<MBeanNotificationInfo>(); for (AlertGenerator generator : alertGenerators) { String className = generator.getClassName(); LinkedHashMap<String,String> alerts = generator.getAlerts(); for (String type : alerts.keySet()) { String[] types = { type }; String description = alerts.get(type); notifications.add(new MBeanNotificationInfo(types, className, description)); } } MBeanNotificationInfo[] mBeanNotifications = new MBeanNotificationInfo[notifications.size()]; notifications.toArray(mBeanNotifications); ArrayList<MBeanOperationInfo> ops = new ArrayList<MBeanOperationInfo>(); for (InvokableComponent component : invokableComponents) { for (InvokableMethod method : component.getOperationSignatures()) { ops.add(method.toOperationInfo()); } } MBeanOperationInfo[] mBeanOperations = new MBeanOperationInfo[ops.size()]; ops.toArray(mBeanOperations); MBeanConstructorInfo[] mBeanConstructors = new MBeanConstructorInfo[0]; return new MBeanInfo(CLASS_NAME, "Configurable Attributes for " + configEntryDN.toString(), mBeanAttributes, mBeanConstructors, mBeanOperations, mBeanNotifications); } /** * Get the client JMX connection to use. Returns null if an Exception is * caught or if the AccessControlContext subject is null. * * @return The JmxClientConnection. */ private ClientConnection getClientConnection() { ClientConnection clientConnection = null; java.security.AccessControlContext acc = java.security.AccessController .getContext(); try { javax.security.auth.Subject subject = javax.security.auth.Subject .getSubject(acc); if (subject != null) { Set<?> privateCreds = subject.getPrivateCredentials(Credential.class); clientConnection = ((Credential) privateCreds.iterator().next()) .getClientConnection(); } } catch (Exception e) { } return clientConnection; } }