1 /* 2 * Copyright (c) 2005, 2021, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.jmx.mbeanserver; 27 28 import java.security.AccessController; 29 import java.util.Arrays; 30 import java.util.Collections; 31 import java.util.List; 32 import java.util.Map; 33 import javax.management.AttributeNotFoundException; 34 import javax.management.InvalidAttributeValueException; 35 import javax.management.MBeanException; 36 import javax.management.MBeanInfo; 37 import javax.management.ReflectionException; 38 39 import static com.sun.jmx.mbeanserver.Util.*; 40 41 /** 42 * Per-MBean-interface behavior. A single instance of this class can be shared 43 * by all MBeans of the same kind (Standard MBean or MXBean) that have the same 44 * MBean interface. 45 * 46 * @since 1.6 47 */ 48 final class PerInterface<M> { PerInterface(Class<?> mbeanInterface, MBeanIntrospector<M> introspector, MBeanAnalyzer<M> analyzer, MBeanInfo mbeanInfo)49 PerInterface(Class<?> mbeanInterface, MBeanIntrospector<M> introspector, 50 MBeanAnalyzer<M> analyzer, MBeanInfo mbeanInfo) { 51 this.mbeanInterface = mbeanInterface; 52 this.introspector = introspector; 53 this.mbeanInfo = mbeanInfo; 54 analyzer.visit(new InitMaps()); 55 } 56 getMBeanInterface()57 Class<?> getMBeanInterface() { 58 return mbeanInterface; 59 } 60 getMBeanInfo()61 MBeanInfo getMBeanInfo() { 62 return mbeanInfo; 63 } 64 isMXBean()65 boolean isMXBean() { 66 return introspector.isMXBean(); 67 } 68 getAttribute(Object resource, String attribute, Object cookie)69 Object getAttribute(Object resource, String attribute, Object cookie) 70 throws AttributeNotFoundException, 71 MBeanException, 72 ReflectionException { 73 74 final M cm = getters.get(attribute); 75 if (cm == null) { 76 final String msg; 77 if (setters.containsKey(attribute)) 78 msg = "Write-only attribute: " + attribute; 79 else 80 msg = "No such attribute: " + attribute; 81 throw new AttributeNotFoundException(msg); 82 } 83 return introspector.invokeM(cm, resource, (Object[]) null, cookie); 84 } 85 setAttribute(Object resource, String attribute, Object value, Object cookie)86 void setAttribute(Object resource, String attribute, Object value, 87 Object cookie) 88 throws AttributeNotFoundException, 89 InvalidAttributeValueException, 90 MBeanException, 91 ReflectionException { 92 93 final M cm = setters.get(attribute); 94 if (cm == null) { 95 final String msg; 96 if (getters.containsKey(attribute)) 97 msg = "Read-only attribute: " + attribute; 98 else 99 msg = "No such attribute: " + attribute; 100 throw new AttributeNotFoundException(msg); 101 } 102 introspector.invokeSetter(attribute, cm, resource, value, cookie); 103 } 104 invoke(Object resource, String operation, Object[] params, String[] signature, Object cookie)105 Object invoke(Object resource, String operation, Object[] params, 106 String[] signature, Object cookie) 107 throws MBeanException, ReflectionException { 108 109 final List<MethodAndSig> list = ops.get(operation); 110 if (list == null) { 111 final String msg = "No such operation: " + operation; 112 return noSuchMethod(msg, resource, operation, params, signature, 113 cookie); 114 } 115 if (signature == null) 116 signature = new String[0]; 117 MethodAndSig found = null; 118 for (MethodAndSig mas : list) { 119 if (Arrays.equals(mas.signature, signature)) { 120 found = mas; 121 break; 122 } 123 } 124 if (found == null) { 125 final String badSig = sigString(signature); 126 final String msg; 127 if (list.size() == 1) { // helpful exception message 128 msg = "Signature mismatch for operation " + operation + 129 ": " + badSig + " should be " + 130 sigString(list.get(0).signature); 131 } else { 132 msg = "Operation " + operation + " exists but not with " + 133 "this signature: " + badSig; 134 } 135 return noSuchMethod(msg, resource, operation, params, signature, 136 cookie); 137 } 138 return introspector.invokeM(found.method, resource, params, cookie); 139 } 140 141 /* 142 * This method is called when invoke doesn't find the named method. 143 * Before throwing an exception, we check to see whether the 144 * jmx.invoke.getters property is set, and if so whether the method 145 * being invoked might be a getter or a setter. If so we invoke it 146 * and return the result. This is for compatibility 147 * with code based on JMX RI 1.0 or 1.1 which allowed invoking getters 148 * and setters. It is *not* recommended that new code use this feature. 149 * 150 * Since this method is either going to throw an exception or use 151 * functionality that is strongly discouraged, we consider that its 152 * performance is not very important. 153 * 154 * A simpler way to implement the functionality would be to add the getters 155 * and setters to the operations map when jmx.invoke.getters is set. 156 * However, that means that the property is consulted when an MBean 157 * interface is being introspected and not thereafter. Previously, 158 * the property was consulted on every invocation. So this simpler 159 * implementation could potentially break code that sets and unsets 160 * the property at different times. 161 */ 162 @SuppressWarnings("removal") noSuchMethod(String msg, Object resource, String operation, Object[] params, String[] signature, Object cookie)163 private Object noSuchMethod(String msg, Object resource, String operation, 164 Object[] params, String[] signature, 165 Object cookie) 166 throws MBeanException, ReflectionException { 167 168 // Construct the exception that we will probably throw 169 final NoSuchMethodException nsme = 170 new NoSuchMethodException(operation + sigString(signature)); 171 final ReflectionException exception = 172 new ReflectionException(nsme, msg); 173 174 if (introspector.isMXBean()) 175 throw exception; // No compatibility requirement here 176 177 // Is the compatibility property set? 178 GetPropertyAction act = new GetPropertyAction("jmx.invoke.getters"); 179 String invokeGettersS; 180 try { 181 invokeGettersS = AccessController.doPrivileged(act); 182 } catch (Exception e) { 183 // We don't expect an exception here but if we get one then 184 // we'll simply assume that the property is not set. 185 invokeGettersS = null; 186 } 187 if (invokeGettersS == null) 188 throw exception; 189 190 int rest = 0; 191 Map<String, M> methods = null; 192 if (signature == null || signature.length == 0) { 193 if (operation.startsWith("get")) 194 rest = 3; 195 else if (operation.startsWith("is")) 196 rest = 2; 197 if (rest != 0) 198 methods = getters; 199 } else if (signature.length == 1 && 200 operation.startsWith("set")) { 201 rest = 3; 202 methods = setters; 203 } 204 205 if (rest != 0) { 206 String attrName = operation.substring(rest); 207 M method = methods.get(attrName); 208 if (method != null && introspector.getName(method).equals(operation)) { 209 String[] msig = introspector.getSignature(method); 210 if ((signature == null && msig.length == 0) || 211 Arrays.equals(signature, msig)) { 212 return introspector.invokeM(method, resource, params, cookie); 213 } 214 } 215 } 216 217 throw exception; 218 } 219 sigString(String[] signature)220 private String sigString(String[] signature) { 221 StringBuilder b = new StringBuilder("("); 222 if (signature != null) { 223 for (String s : signature) { 224 if (b.length() > 1) 225 b.append(", "); 226 b.append(s); 227 } 228 } 229 return b.append(")").toString(); 230 } 231 232 /** 233 * Visitor that sets up the method maps (operations, getters, setters). 234 */ 235 private class InitMaps implements MBeanAnalyzer.MBeanVisitor<M> { visitAttribute(String attributeName, M getter, M setter)236 public void visitAttribute(String attributeName, 237 M getter, 238 M setter) { 239 if (getter != null) { 240 introspector.checkMethod(getter); 241 final Object old = getters.put(attributeName, getter); 242 assert(old == null); 243 } 244 if (setter != null) { 245 introspector.checkMethod(setter); 246 final Object old = setters.put(attributeName, setter); 247 assert(old == null); 248 } 249 } 250 visitOperation(String operationName, M operation)251 public void visitOperation(String operationName, 252 M operation) { 253 introspector.checkMethod(operation); 254 final String[] sig = introspector.getSignature(operation); 255 final MethodAndSig mas = new MethodAndSig(); 256 mas.method = operation; 257 mas.signature = sig; 258 List<MethodAndSig> list = ops.get(operationName); 259 if (list == null) 260 list = Collections.singletonList(mas); 261 else { 262 if (list.size() == 1) 263 list = newList(list); 264 list.add(mas); 265 } 266 ops.put(operationName, list); 267 } 268 } 269 270 private class MethodAndSig { 271 M method; 272 String[] signature; 273 } 274 275 private final Class<?> mbeanInterface; 276 private final MBeanIntrospector<M> introspector; 277 private final MBeanInfo mbeanInfo; 278 private final Map<String, M> getters = newMap(); 279 private final Map<String, M> setters = newMap(); 280 private final Map<String, List<MethodAndSig>> ops = newMap(); 281 } 282