/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved. * * The contents of this file are subject to the terms of either the GNU * General Public License Version 2 only ("GPL") or the Common Development * and Distribution License("CDDL") (collectively, the "License"). You * may not use this file except in compliance with the License. You can * obtain a copy of the License at * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html * or packager/legal/LICENSE.txt. See the License for the specific * language governing permissions and limitations under the License. * * When distributing the software, include this License Header Notice in each * file and include the License file at packager/legal/LICENSE.txt. * * GPL Classpath Exception: * Oracle designates this particular file as subject to the "Classpath" * exception as provided by Oracle in the GPL Version 2 section of the License * file that accompanied this code. * * Modifications: * If applicable, add the following below the License Header, with the fields * enclosed by brackets [] replaced by your own identifying information: * "Portions Copyright [year] [name of copyright owner]" * * Contributor(s): * If you wish your version of this file to be governed by only the CDDL or * only the GPL Version 2, indicate your decision by adding "[Contributor] * elects to include this software in this distribution under the [CDDL or GPL * Version 2] license." If you don't indicate a single choice of license, a * recipient has the option to distribute your version of this file under * either the CDDL, the GPL Version 2 or to extend the choice of license to * its licensees as provided above. However, if you add GPL Version 2 code * and therefore, elected the GPL Version 2 license, then the option applies * only if the new code is made subject to such option by the copyright * holder. */ /* * $Header: /cvs/glassfish/admin/mbeanapi-impl/tests/org.glassfish.admin.amxtest/base/GenericTest.java,v 1.8 2007/05/05 05:23:53 tcfujii Exp $ * $Revision: 1.8 $ * $Date: 2007/05/05 05:23:53 $ */ package org.glassfish.admin.amxtest.base; import com.sun.appserv.management.util.jmx.JMXUtil; import com.sun.appserv.management.util.jmx.ReadWriteAttributeFilter; import com.sun.appserv.management.util.misc.ArrayConversion; import com.sun.appserv.management.util.misc.ArrayUtil; import com.sun.appserv.management.util.misc.ClassUtil; import com.sun.appserv.management.util.misc.ExceptionUtil; import com.sun.appserv.management.util.stringifier.ArrayStringifier; import org.glassfish.admin.amxtest.AMXTestBase; import org.glassfish.admin.amxtest.Capabilities; import javax.management.Attribute; import javax.management.AttributeList; import javax.management.InstanceNotFoundException; import javax.management.MBeanAttributeInfo; import javax.management.MBeanInfo; import javax.management.MBeanOperationInfo; import javax.management.MBeanServerConnection; import javax.management.ObjectName; import java.io.IOException; import java.io.NotSerializableException; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; /** */ public final class GenericTest extends AMXTestBase { final boolean mDoInfo; final boolean mDoAttributes; final boolean mDoOperations; final boolean mwarnings; public GenericTest() throws IOException { mDoInfo = true; mDoAttributes = true; mDoOperations = true; mwarnings = true; } public static Capabilities getCapabilities() { return getOfflineCapableCapabilities(true); } private Map<String, Throwable> validateAttributesSingly( final ObjectName objectName, final String[] attrNames, Map<String, Throwable> failures, Map<String, Throwable> warnings) throws Exception { MBeanServerConnection conn = getConnection(); for (int i = 0; i < attrNames.length; ++i) { final String attrName = attrNames[i]; try { final Object a = conn.getAttribute(objectName, attrName); if (a == null) { // null is legal, apparently } } catch (NotSerializableException e) { warnings.put(attrName, e); } catch (IOException e) { failures.put(attrName, e); } catch (Exception e) { failures.put(attrName, e); } } return (failures); } private String getExceptionMsg(final Throwable e) { String msg = null; if (e instanceof IOException) { msg = "received an exception of class " + e.getClass().getName(); if (shouldPrintStackTraces()) { msg = msg + "Stack trace = \n" + ExceptionUtil.getStackTrace(ExceptionUtil.getRootCause(e)); } } else { msg = "threw an Exception of type " + e.getClass().getName() + ", message = " + e.getMessage(); if (shouldPrintStackTraces()) { msg = msg + "\n" + ExceptionUtil.getStackTrace(e); } } final Throwable rootCause = ExceptionUtil.getRootCause(e); if (rootCause != e) { msg = msg + "...\nRoot cause was exception of type " + e.getClass().getName() + ", message = " + rootCause.getMessage(); if (shouldPrintStackTraces()) { msg = msg + "\n" + ExceptionUtil.getStackTrace(rootCause); } } return (msg); } MBeanAttributeInfo findAttributeInfo( final MBeanAttributeInfo[] infos, String attrName) { MBeanAttributeInfo info = null; for (int i = 0; i < infos.length; ++i) { if (infos[i] != null && infos[i].getName().equals(attrName)) { info = infos[i]; break; } } assert (info != null); return (info); } private void displayAttributeFailuresOrWarnings( final boolean failure, final ObjectName objectName, final MBeanAttributeInfo[] infos, final Map<String, Throwable> problems) throws Exception { trace(""); trace(problems.keySet().size() + (failure ? " Failures: " : " Warnings: ") + objectName); int i = 0; for (final String attrName : problems.keySet()) { final Throwable t = problems.get(attrName); final MBeanAttributeInfo info = findAttributeInfo(infos, attrName); final String prefix = "(" + (i + 1) + ")" + " getting Attribute \"" + attrName + "\" of type " + info.getType() + " "; if (t == null) { trace(prefix + "returned null"); } else { trace(prefix + getExceptionMsg(t)); } ++i; } } private boolean validateMBeanInfo( final ObjectName objectName, final MBeanInfo info) { boolean valid = true; if (ArrayUtil.arrayContainsNulls(info.getAttributes())) { warning("MBean has nulls in its MBeanAttributeInfo[]: " + objectName); valid = false; } if (ArrayUtil.arrayContainsNulls(info.getConstructors())) { warning("MBean has nulls in its MBeanConstructorInfo[]: " + objectName); valid = false; } if (ArrayUtil.arrayContainsNulls(info.getOperations())) { warning("MBean has nulls in its MBeanOperationInfo[]: " + objectName); valid = false; } if (ArrayUtil.arrayContainsNulls(info.getNotifications())) { warning("MBean has nulls in its MBeanNotificationInfo[]: " + objectName); valid = false; } return (valid); } static final private String SECTION_LINE = "--------------------------------------------------------------------------------"; private void printDuplicateAttributes( final ObjectName objectName, MBeanAttributeInfo[] attrInfos, String name) { String msg = "MBean " + quote(objectName) + " has the same Attribute listed more than once:\n"; for (int i = 0; i < attrInfos.length; ++i) { final MBeanAttributeInfo a = attrInfos[i]; if (a.getName().equals(name)) { msg = msg + name + ": " + a.getType() + ", " + quote(a.getDescription()); } } warning(msg); } private boolean validateUniqueAttributeNames( final ObjectName objectName, MBeanAttributeInfo[] attrInfos) { boolean valid = true; final MBeanAttributeInfo[] infos = JMXUtil.filterAttributeInfos(attrInfos, ReadWriteAttributeFilter.READABLE_FILTER); final String[] names = JMXUtil.getAttributeNames(infos); if (ArrayConversion.arrayToSet(names).size() != attrInfos.length) { final Set<String> set = new HashSet<String>(); for (int i = 0; i < names.length; ++i) { final String name = names[i]; if (set.contains(name)) { valid = false; printDuplicateAttributes(objectName, attrInfos, name); } else { set.add(name); } } set.clear(); } return (valid); } private boolean validateMissingAndEmptyAttributeNames(final ObjectName objectName) { boolean valid = true; final MBeanServerConnection conn = getConnection(); AttributeList attrs = null; try { attrs = conn.getAttributes(objectName, new String[0]); if (attrs == null) { warning("MBean " + quote(objectName) + " returned NULL for an empty AttributeList"); valid = false; } else if (attrs.size() != 0) { warning("MBean " + quote(objectName) + " returned attributes for an empty AttributeList"); valid = false; } } catch (Exception e) { valid = false; warning("MBean " + quote(objectName) + " threw an exception getting an empty attribute list"); } try { final String notFoundName = "bogus." + System.currentTimeMillis(); attrs = conn.getAttributes(objectName, new String[]{notFoundName}); if (attrs == null) { warning("MBean " + quote(objectName) + " returned NULL for a missing Attribute"); valid = false; } else if (attrs.size() != 0) { warning("MBean " + quote(objectName) + " returned attributes for a non-existent name"); valid = false; } } catch (Exception e) { valid = false; warning("MBean " + quote(objectName) + " threw an exception when getAttributes() was called with a " + "non-existent Attribute, exception class = " + e.getClass().getName()); } return (valid); } private boolean validateAttributeTypes( final ObjectName objectName, final AttributeList attrs, final MBeanAttributeInfo[] attrInfos) throws Exception { boolean valid = true; final Map<String, MBeanAttributeInfo> attrInfosMap = JMXUtil.attributeInfosToMap(attrInfos); final Iterator iter = attrs.iterator(); while (iter.hasNext()) { final Attribute attr = (Attribute) iter.next(); final String name = attr.getName(); final Object value = attr.getValue(); final MBeanAttributeInfo attrInfo = (MBeanAttributeInfo) attrInfosMap.get(name); if (attrInfo == null) { valid = false; warning("MBean " + objectName + " returned an Attribute not " + "declared in its MBeanInfo: " + name); } else if (value != null) { final String typeName = attrInfo.getType(); final Class<?> infoClass = ClassUtil.getClassFromName(typeName); final Class<?> valueClass = value.getClass(); if (infoClass == null) { valid = false; warning("Can't find class for: " + typeName); } else if (!infoClass.isAssignableFrom(valueClass)) { final Class<?> objectClass = ClassUtil.PrimitiveClassToObjectClass(infoClass); if (!objectClass.isAssignableFrom(valueClass)) { valid = false; warning("MBean " + objectName + " returned Attribute " + name + "=" + value + " of class " + value.getClass().getName() + " not matching its MBeanInfo: " + infoClass.getName()); } } } } return (valid); } private boolean validateAttributes( final ObjectName objectName, final MBeanAttributeInfo[] attrInfos) throws Exception { boolean valid = true; final MBeanAttributeInfo[] readableInfos = JMXUtil.filterAttributeInfos(attrInfos, ReadWriteAttributeFilter.READABLE_FILTER); final String[] attrNames = JMXUtil.getAttributeNames(readableInfos); Arrays.sort(attrNames); if (attrNames.length != 0) { // if we can fetch all the attributes, then the MBean is OK; // try this first for efficiency try { //trace( objectName.getKeyProperty( "j2eeType" ) + ": " + attrNames.length ); final AttributeList attrs = getConnection().getAttributes(objectName, attrNames); if (attrs == null) { warning("MBean " + quote(objectName) + " returned NULL for its AttributeList"); valid = false; } else if (attrs.size() != readableInfos.length) { // mismatch between claimed number of attributes and actual final ArrayStringifier as = new ArrayStringifier(", ", true); final String claimedString = as.stringify(attrNames); final Set<String> actualSet = JMXUtil.attributeListToValueMap(attrs).keySet(); final Set<String> missingSet = ArrayConversion.arrayToSet(attrNames); missingSet.removeAll(actualSet); final String[] missingNames = (String[]) ArrayConversion.setToArray(missingSet, true); Arrays.sort(missingNames); final String missingString = as.stringify(missingNames); warning("MBean " + quote(objectName) + " did not supply the " + missingNames.length + " attributes " + missingString); } valid = validateAttributeTypes(objectName, attrs, readableInfos); } catch (Exception e) { trace(SECTION_LINE); final String msg = "getAttributes() failed on " + quote(objectName) + ", exception =\n" + e; if (e instanceof NotSerializableException) { warning(msg); } else { warning(msg); valid = false; } // do them one-at-a time to see where failure occurs final Map<String, Throwable> failures = new HashMap<String, Throwable>(); final Map<String, Throwable> warnings = new HashMap<String, Throwable>(); validateAttributesSingly(objectName, attrNames, failures, warnings); trace("Validating attributes one-at-a-time using getAttribute() for " + quote(objectName)); if (failures.size() == 0 && warnings.size() == 0) { warning(" during getAttributes(" + ArrayStringifier.stringify(attrNames, ",") + ") for: " + objectName + " (but Attributes work when queried one-at-a-time).\nIt " + getExceptionMsg(e)); } if (failures.size() != 0) { displayAttributeFailuresOrWarnings(true, objectName, readableInfos, failures); } if (warnings.size() != 0) { displayAttributeFailuresOrWarnings(false, objectName, readableInfos, warnings); } trace(SECTION_LINE); } } else { valid = true; } if (!validateUniqueAttributeNames(objectName, attrInfos)) { valid = false; } if (!validateMissingAndEmptyAttributeNames(objectName)) { valid = false; } return (valid); } void checkObjectNameReturnValue( MBeanServerConnection conn, ObjectName callee, MBeanOperationInfo operationInfo, ObjectName resultOfCall) throws Exception { try { printVerbose("checking MBean info for: " + resultOfCall); final MBeanInfo mbeanInfo = conn.getMBeanInfo(resultOfCall); } catch (InstanceNotFoundException e) { trace("WARNING: MBean " + resultOfCall + " returned from " + operationInfo.getReturnType() + " " + operationInfo.getName() + "() does not exist"); } catch (Exception e) { trace("WARNING: MBean " + resultOfCall + " returned from " + operationInfo.getReturnType() + " " + operationInfo.getName() + "() can't supply MBeanInfo: " + getExceptionMsg(e) ); if (e instanceof IOException) { throw (IOException) e; } } } void checkGetterResult( MBeanServerConnection conn, ObjectName callee, MBeanOperationInfo operationInfo, Object resultOfCall) throws Exception { if (resultOfCall instanceof ObjectName) { final ObjectName name = (ObjectName) resultOfCall; checkObjectNameReturnValue(conn, callee, operationInfo, name); } else if (resultOfCall instanceof ObjectName[]) { final ObjectName[] names = (ObjectName[]) resultOfCall; for (int i = 0; i < names.length; ++i) { checkObjectNameReturnValue(conn, callee, operationInfo, names[i]); } } } private boolean validateGetters( final ObjectName objectName, final MBeanOperationInfo[] operationInfos) throws Exception { boolean valid = true; MBeanServerConnection conn = getConnection(); for (int i = 0; i < operationInfos.length; ++i) { final MBeanOperationInfo info = operationInfos[i]; if (JMXUtil.isGetter(info)) { boolean opValid = false; try { printVerbose("invoking getter: " + info.getName() + "()"); final Object result = conn.invoke(objectName, info.getName(), null, null); checkGetterResult(conn, objectName, info, result); } catch (Exception e) { warning("Failure: calling " + info.getName() + "() on " + objectName + ": " + getExceptionMsg(e)); if (e instanceof IOException) { throw ((IOException) e); } valid = false; } } } return (valid); } boolean shouldPrintStackTraces() { return (true); } private boolean validate(final ObjectName objectName) throws Exception { boolean valid = true; MBeanServerConnection conn = getConnection(); MBeanInfo info = null; try { info = conn.getMBeanInfo(objectName); } catch (Exception e) { valid = false; warning(" during getMBeanInfo() for: " + objectName + "\n" + " message = " + e.getMessage()); // abort--the connection has died throw e; } if (mDoInfo && !validateMBeanInfo(objectName, info)) { trace("validateMBeanInfo failed for: " + objectName); valid = false; } if (mDoAttributes && !validateAttributes(objectName, info.getAttributes())) { trace("validateAttributes failed for: " + objectName); valid = false; } if (mDoOperations && !validateGetters(objectName, info.getOperations())) { trace("validateGetters failed for: " + objectName); valid = false; } return (valid); } private void validate(final ObjectName[] objectNames) throws Exception { int failureCount = 0; trace("Validating: "); if (mDoInfo) { trace("- MBeanInfo"); } if (mDoAttributes) { trace("- Attributes"); } if (mDoOperations) { trace("- Operations (getters)"); } trace(""); for (int i = 0; i < objectNames.length; ++i) { final ObjectName objectName = objectNames[i]; printVerbose("Validating: " + objectName); if (!shouldTest(objectName)) { notTested(objectName); continue; } final boolean valid = validate(objectName); if (!valid) { ++failureCount; } } trace("Total mbeans failing: " + failureCount); } public void testGenerically() throws Exception { final Set<ObjectName> all = getTestUtil().getAllObjectNames(); final ObjectName[] allObjectNames = new ObjectName[all.size()]; all.toArray(allObjectNames); validate(allObjectNames); } }