--- old/application/org.openjdk.jmc.rjmx/src/main/java/org/openjdk/jmc/rjmx/internal/RJMXConnection.java 2019-02-13 10:07:37.000000000 +0100 +++ new/application/org.openjdk.jmc.rjmx/src/main/java/org/openjdk/jmc/rjmx/internal/RJMXConnection.java 2019-02-13 10:07:37.000000000 +0100 @@ -75,7 +75,6 @@ import org.openjdk.jmc.rjmx.IServerDescriptor; import org.openjdk.jmc.rjmx.RJMXPlugin; import org.openjdk.jmc.rjmx.services.IOperation; -import org.openjdk.jmc.rjmx.services.internal.MBeanOperationsWrapper; import org.openjdk.jmc.rjmx.subscription.IMBeanHelperService; import org.openjdk.jmc.rjmx.subscription.IMBeanServerChangeListener; import org.openjdk.jmc.rjmx.subscription.IMRIService; @@ -578,7 +577,7 @@ */ public Collection getOperations(ObjectName mbean) throws Exception { MBeanServerConnection srv = ensureConnected(); - return new MBeanOperationsWrapper(mbean, srv.getMBeanInfo(mbean).getOperations(), srv); + return MBeanOperationWrapper.createOperations(mbean, srv.getMBeanInfo(mbean).getOperations(), srv); } IMRIService getMRIService() { --- /dev/null 2019-02-13 10:07:38.000000000 +0100 +++ new/application/org.openjdk.jmc.rjmx/src/main/java/org/openjdk/jmc/rjmx/internal/MBeanOperationWrapper.java 2019-02-13 10:07:37.000000000 +0100 @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * The contents of this file are subject to the terms of either the Universal Permissive License + * v 1.0 as shown at http://oss.oracle.com/licenses/upl + * + * or the following license: + * + * Redistribution and use in source and binary forms, with or without modification, are permitted + * provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions + * and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of + * conditions and the following disclaimer in the documentation and/or other materials provided with + * the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to + * endorse or promote products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package org.openjdk.jmc.rjmx.internal; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; + +import javax.management.MBeanFeatureInfo; +import javax.management.MBeanOperationInfo; +import javax.management.MBeanParameterInfo; +import javax.management.MBeanServerConnection; +import javax.management.ObjectName; + +import org.openjdk.jmc.common.util.TypeHandling; +import org.openjdk.jmc.rjmx.services.IOperation; +import org.openjdk.jmc.rjmx.services.IllegalOperandException; +import org.openjdk.jmc.rjmx.services.internal.AbstractOperation; +import org.openjdk.jmc.rjmx.services.internal.Messages; +import org.openjdk.jmc.rjmx.util.internal.SimpleAttributeInfo; + +final class MBeanOperationWrapper extends AbstractOperation { + private static final String IMPACT = ".vmImpact"; //$NON-NLS-1$ + private static final int MAX_DESCRIPTORS = 8; + private static final int MAX_LINE_LENGTH = 100; + private static final String ELLIPSIS_STRING = "..."; //$NON-NLS-1$ + + private final MBeanServerConnection connection; + private final ObjectName objectName; + + private MBeanOperationWrapper(MBeanServerConnection connection, ObjectName objectName, MBeanOperationInfo info) { + super(info.getName(), convertDescription(info), info.getReturnType(), convertArguments(info), + convertImpact(info)); + this.connection = connection; + this.objectName = objectName; + } + + @Override + public Callable getInvocator(final Object ... argVals) throws IllegalOperandException { + List params = getSignature(); + final StringBuilder argString = new StringBuilder("("); //$NON-NLS-1$ + if (argVals.length < params.size()) { + // Argument list is shorter than the signature + throw new IllegalOperandException(params.subList(argVals.length, params.size())); + } + final String[] sig = new String[params.size()]; + for (int i = 0; i < params.size(); i++) { + sig[i] = params.get(i).getType(); + if (argVals[i] != null) { + argString.append(' ').append(describeValue(argVals[i])).append(','); + } else if (TypeHandling.isPrimitive(sig[i])) { + // Argument value of primitive type is null + IllegalOperandException ex = new IllegalOperandException(params.get(i)); + while (++i < params.size()) { + // Check for other attributes with the same error + if (argVals[i] == null && TypeHandling.isPrimitive(params.get(i).getType())) { + ex.addInvalidValue(params.get(i)); + } + } + throw ex; + } + } + if (argString.charAt(argString.length() - 1) == ',') { + argString.deleteCharAt(argString.length() - 1); + } + argString.append(" )"); //$NON-NLS-1$ + + return new Callable() { + + @Override + public Object call() throws Exception { + return connection.invoke(objectName, getName(), argVals, sig); + } + + @Override + public String toString() { + return getName() + argString.toString().replace("\n", "\\n").replace("\r", "\\r"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + } + }; + } + + static Collection createOperations( + ObjectName objectName, MBeanOperationInfo[] operations, MBeanServerConnection connection) { + List wrappedOperations = new ArrayList<>(); + for (MBeanOperationInfo info : operations) { + wrappedOperations.add(new MBeanOperationWrapper(connection, objectName, info)); + } + return wrappedOperations; + } + + /** + * @return {@code string} if it is not {@code null} and it is not an empty or blank string, + * otherwise {@code defaultString} + */ + private static String asNonNullNorEmptyString(String string, String defaultString) { + return (string != null && string.trim().length() > 0) ? string : defaultString; + } + + private static String describeValue(Object o) { + if (o.getClass().isArray()) { + return o.getClass().getComponentType().getName() + "[" + Array.getLength(o) + "]"; //$NON-NLS-1$ //$NON-NLS-2$ + } else if (o instanceof String) { + return "\"" + o + "\""; //$NON-NLS-1$ //$NON-NLS-2$ + } else { + return o.toString(); + } + } + + private static OperationImpact convertImpact(MBeanOperationInfo info) { + for (String name : info.getDescriptor().getFieldNames()) { + if (name.endsWith(IMPACT)) { + Object impactField = info.getDescriptor().getFieldValue(name); + if (impactField != null) { + String impact = impactField.toString(); + if (impact.startsWith("Low")) { //$NON-NLS-1$ + return OperationImpact.IMPACT_LOW; + } + if (impact.startsWith("Medium")) { //$NON-NLS-1$ + return OperationImpact.IMPACT_MEDIUM; + } + if (impact.startsWith("High")) { //$NON-NLS-1$ + return OperationImpact.IMPACT_HIGH; + } + } + } + } + return OperationImpact.IMPACT_UNKNOWN; + } + + private static List convertArguments(MBeanOperationInfo info) { + MBeanParameterInfo[] sign = info.getSignature(); + List signature = new ArrayList<>(sign.length); + for (MBeanParameterInfo paramInfo : sign) { + // Use name primarily. Use description as name only if name is null or empty. + String name = asNonNullNorEmptyString(paramInfo.getName(), + asNonNullNorEmptyString(paramInfo.getDescription(), "")); //$NON-NLS-1$ + signature.add(new SimpleAttributeInfo(name, paramInfo.getType(), convertDescription(paramInfo))); + } + return signature; + } + + private static String convertDescription(MBeanFeatureInfo info) { + // FIXME: Building tool tips and descriptors should be unified into a toolkit + StringBuilder sb = new StringBuilder(); + if (info.getDescriptor() != null && info.getDescriptor().getFields() != null) { + String[] fields = info.getDescriptor().getFields(); + if (fields.length > 0) { + // TODO: This is a workaround to get the descriptors to UI-layer. Should be handled using some adaptive mechanism. + sb.append(Messages.MBeanOperationsWrapper_DESCRIPTOR).append(":\n "); //$NON-NLS-1$ + for (int i = 0; i < Math.min(fields.length, MAX_DESCRIPTORS); i++) { + String str = fields[i]; + int cur = 0; + int newLine = 0; + while (cur < str.length() && newLine != -1) { + newLine = str.indexOf('\n', cur); + if (newLine == -1) { + sb.append(shorten(str.substring(cur))).append("\n "); //$NON-NLS-1$ + } else { + sb.append(shorten(str.substring(cur, newLine))).append("\n "); //$NON-NLS-1$ + cur = newLine + 1; + } + } + } + } + } + return shorten(info.getDescription()) + "\n " + sb.toString().trim(); //$NON-NLS-1$ + } + + private static String shorten(String s) { + if (s == null) { + return ""; //$NON-NLS-1$ + } else if (s.length() > MAX_LINE_LENGTH) { + return (s.subSequence(0, MAX_LINE_LENGTH - ELLIPSIS_STRING.length()) + ELLIPSIS_STRING); + } else { + return s; + } + } +} --- old/application/org.openjdk.jmc.rjmx/src/main/java/org/openjdk/jmc/rjmx/services/internal/MBeanOperationsWrapper.java 2019-02-13 10:07:38.000000000 +0100 +++ /dev/null 2019-02-13 10:07:38.000000000 +0100 @@ -1,222 +0,0 @@ -/* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * The contents of this file are subject to the terms of either the Universal Permissive License - * v 1.0 as shown at http://oss.oracle.com/licenses/upl - * - * or the following license: - * - * Redistribution and use in source and binary forms, with or without modification, are permitted - * provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, this list of conditions - * and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright notice, this list of - * conditions and the following disclaimer in the documentation and/or other materials provided with - * the distribution. - * - * 3. Neither the name of the copyright holder nor the names of its contributors may be used to - * endorse or promote products derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -package org.openjdk.jmc.rjmx.services.internal; - -import java.io.IOException; -import java.io.NotSerializableException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.List; -import java.util.concurrent.Callable; - -import javax.management.MBeanFeatureInfo; -import javax.management.MBeanOperationInfo; -import javax.management.MBeanParameterInfo; -import javax.management.MBeanServerConnection; -import javax.management.ObjectName; - -import org.openjdk.jmc.common.util.TypeHandling; -import org.openjdk.jmc.rjmx.services.IOperation; -import org.openjdk.jmc.rjmx.services.IOperation.OperationImpact; -import org.openjdk.jmc.rjmx.services.IllegalOperandException; -import org.openjdk.jmc.rjmx.util.internal.SimpleAttributeInfo; - -public final class MBeanOperationsWrapper extends ArrayList { - - private static final int MAX_DESCRIPTORS = 8; - private static final int MAX_LINE_LENGTH = 100; - private static final String ELLIPSIS_STRING = "..."; //$NON-NLS-1$ - private static final long serialVersionUID = -8499920218074514535L; - private static final String IMPACT = ".vmImpact"; //$NON-NLS-1$ - private final ObjectName bean; - private final transient MBeanServerConnection connection; - - public MBeanOperationsWrapper(ObjectName bean, MBeanOperationInfo[] operations, MBeanServerConnection connection) { - super(operations.length); - this.connection = connection; - this.bean = bean; - for (MBeanOperationInfo info : operations) { - add(new MBeanOperation(info)); - } - } - - private static String convertDescription(MBeanFeatureInfo info) { - // FIXME: Building tool tips and descriptors should be unified into a toolkit - StringBuilder sb = new StringBuilder(); - if (info.getDescriptor() != null && info.getDescriptor().getFields() != null) { - String[] fields = info.getDescriptor().getFields(); - if (fields.length > 0) { - // TODO: This is a workaround to get the descriptors to UI-layer. Should be handled using some adaptive mechanism. - sb.append(Messages.MBeanOperationsWrapper_DESCRIPTOR).append(":\n "); //$NON-NLS-1$ - for (int i = 0; i < Math.min(fields.length, MAX_DESCRIPTORS); i++) { - String str = fields[i]; - int cur = 0; - int newLine = 0; - while (cur < str.length() && newLine != -1) { - newLine = str.indexOf('\n', cur); - if (newLine == -1) { - sb.append(shorten(str.substring(cur))).append("\n "); //$NON-NLS-1$ - } else { - sb.append(shorten(str.substring(cur, newLine))).append("\n "); //$NON-NLS-1$ - cur = newLine + 1; - } - } - } - } - } - return shorten(info.getDescription()) + "\n " + sb.toString().trim(); //$NON-NLS-1$ - } - - private static String shorten(String s) { - if (s == null) { - return ""; //$NON-NLS-1$ - } else if (s.length() > MAX_LINE_LENGTH) { - return (s.subSequence(0, MAX_LINE_LENGTH - ELLIPSIS_STRING.length()) + ELLIPSIS_STRING); - } else { - return s; - } - } - - private static List convertArguments(MBeanOperationInfo info) { - MBeanParameterInfo[] sign = info.getSignature(); - List signature = new ArrayList<>(sign.length); - for (MBeanParameterInfo paramInfo : sign) { - // Use name primarily. Use description as name only if name is null or empty. - String name = asNonNullNorEmptyString(paramInfo.getName(), - asNonNullNorEmptyString(paramInfo.getDescription(), "")); //$NON-NLS-1$ - signature.add(new SimpleAttributeInfo(name, paramInfo.getType(), convertDescription(paramInfo))); - } - return signature; - } - - /** - * @return {@code string} if it is not {@code null} and it is not an empty or blank string, - * otherwise {@code defaultString} - */ - private static String asNonNullNorEmptyString(String string, String defaultString) { - return (string != null && string.trim().length() > 0) ? string : defaultString; - } - - private static OperationImpact convertImpact(MBeanOperationInfo info) { - for (String name : info.getDescriptor().getFieldNames()) { - if (name.endsWith(IMPACT)) { - Object impactField = info.getDescriptor().getFieldValue(name); - if (impactField != null) { - String impact = impactField.toString(); - if (impact.startsWith("Low")) { //$NON-NLS-1$ - return OperationImpact.IMPACT_LOW; - } - if (impact.startsWith("Medium")) { //$NON-NLS-1$ - return OperationImpact.IMPACT_MEDIUM; - } - if (impact.startsWith("High")) { //$NON-NLS-1$ - return OperationImpact.IMPACT_HIGH; - } - } - } - } - return OperationImpact.IMPACT_UNKNOWN; - } - - private class MBeanOperation extends AbstractOperation { - - public MBeanOperation(MBeanOperationInfo info) { - super(info.getName(), convertDescription(info), info.getReturnType(), convertArguments(info), - convertImpact(info)); - } - - @Override - public Callable getInvocator(final Object ... argVals) throws IllegalOperandException { - List params = getSignature(); - final StringBuilder argString = new StringBuilder("("); //$NON-NLS-1$ - if (argVals.length < params.size()) { - // Argument list is shorter than the signature - throw new IllegalOperandException(params.subList(argVals.length, params.size())); - } - final String[] sig = new String[params.size()]; - for (int i = 0; i < params.size(); i++) { - sig[i] = params.get(i).getType(); - if (argVals[i] != null) { - argString.append(' ').append(describeValue(argVals[i])).append(','); - } else if (TypeHandling.isPrimitive(sig[i])) { - // Argument value of primitive type is null - IllegalOperandException ex = new IllegalOperandException(params.get(i)); - while (++i < params.size()) { - // Check for other attributes with the same error - if (argVals[i] == null && TypeHandling.isPrimitive(params.get(i).getType())) { - ex.addInvalidValue(params.get(i)); - } - } - throw ex; - } - } - if (argString.charAt(argString.length() - 1) == ',') { - argString.deleteCharAt(argString.length() - 1); - } - argString.append(" )"); //$NON-NLS-1$ - - return new Callable() { - - @Override - public Object call() throws Exception { - return connection.invoke(bean, getName(), argVals, sig); - } - - @Override - public String toString() { - return getName() + argString.toString().replace("\n", "\\n").replace("\r", "\\r"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ - } - }; - } - } - - private static String describeValue(Object o) { - if (o.getClass().isArray()) { - return o.getClass().getComponentType().getName() + "[" + Array.getLength(o) + "]"; //$NON-NLS-1$ //$NON-NLS-2$ - } else if (o instanceof String) { - return "\"" + o + "\""; //$NON-NLS-1$ //$NON-NLS-2$ - } else { - return o.toString(); - } - } - - private void writeObject(ObjectOutputStream out) throws IOException { - throw new NotSerializableException("MBeanOperationsWrappers should not be serialized!"); //$NON-NLS-1$ - } - - private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { - throw new NotSerializableException("MBeanOperationsWrappers should not be serialized!"); //$NON-NLS-1$ - } -}