1/* 2 * Copyright (c) 2005, 2006, 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 26package com.sun.jmx.mbeanserver; 27 28import java.security.AccessController; 29import java.util.Arrays; 30import java.util.Collections; 31import java.util.List; 32import java.util.Map; 33import javax.management.AttributeNotFoundException; 34import javax.management.InvalidAttributeValueException; 35import javax.management.MBeanException; 36import javax.management.MBeanInfo; 37import javax.management.ReflectionException; 38 39import 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 */ 48final class PerInterface<M> { 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 57 Class<?> getMBeanInterface() { 58 return mbeanInterface; 59 } 60 61 MBeanInfo getMBeanInfo() { 62 return mbeanInfo; 63 } 64 65 boolean isMXBean() { 66 return introspector.isMXBean(); 67 } 68 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 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 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 private Object noSuchMethod(String msg, Object resource, String operation, 163 Object[] params, String[] signature, 164 Object cookie) 165 throws MBeanException, ReflectionException { 166 167 // Construct the exception that we will probably throw 168 final NoSuchMethodException nsme = 169 new NoSuchMethodException(operation + sigString(signature)); 170 final ReflectionException exception = 171 new ReflectionException(nsme, msg); 172 173 if (introspector.isMXBean()) 174 throw exception; // No compatibility requirement here 175 176 // Is the compatibility property set? 177 GetPropertyAction act = new GetPropertyAction("jmx.invoke.getters"); 178 String invokeGettersS; 179 try { 180 invokeGettersS = AccessController.doPrivileged(act); 181 } catch (Exception e) { 182 // We don't expect an exception here but if we get one then 183 // we'll simply assume that the property is not set. 184 invokeGettersS = null; 185 } 186 if (invokeGettersS == null) 187 throw exception; 188 189 int rest = 0; 190 Map<String, M> methods = null; 191 if (signature == null || signature.length == 0) { 192 if (operation.startsWith("get")) 193 rest = 3; 194 else if (operation.startsWith("is")) 195 rest = 2; 196 if (rest != 0) 197 methods = getters; 198 } else if (signature.length == 1 && 199 operation.startsWith("set")) { 200 rest = 3; 201 methods = setters; 202 } 203 204 if (rest != 0) { 205 String attrName = operation.substring(rest); 206 M method = methods.get(attrName); 207 if (method != null && introspector.getName(method).equals(operation)) { 208 String[] msig = introspector.getSignature(method); 209 if ((signature == null && msig.length == 0) || 210 Arrays.equals(signature, msig)) { 211 return introspector.invokeM(method, resource, params, cookie); 212 } 213 } 214 } 215 216 throw exception; 217 } 218 219 private String sigString(String[] signature) { 220 StringBuilder b = new StringBuilder("("); 221 if (signature != null) { 222 for (String s : signature) { 223 if (b.length() > 1) 224 b.append(", "); 225 b.append(s); 226 } 227 } 228 return b.append(")").toString(); 229 } 230 231 /** 232 * Visitor that sets up the method maps (operations, getters, setters). 233 */ 234 private class InitMaps implements MBeanAnalyzer.MBeanVisitor<M> { 235 public void visitAttribute(String attributeName, 236 M getter, 237 M setter) { 238 if (getter != null) { 239 introspector.checkMethod(getter); 240 final Object old = getters.put(attributeName, getter); 241 assert(old == null); 242 } 243 if (setter != null) { 244 introspector.checkMethod(setter); 245 final Object old = setters.put(attributeName, setter); 246 assert(old == null); 247 } 248 } 249 250 public void visitOperation(String operationName, 251 M operation) { 252 introspector.checkMethod(operation); 253 final String[] sig = introspector.getSignature(operation); 254 final MethodAndSig mas = new MethodAndSig(); 255 mas.method = operation; 256 mas.signature = sig; 257 List<MethodAndSig> list = ops.get(operationName); 258 if (list == null) 259 list = Collections.singletonList(mas); 260 else { 261 if (list.size() == 1) 262 list = newList(list); 263 list.add(mas); 264 } 265 ops.put(operationName, list); 266 } 267 } 268 269 private class MethodAndSig { 270 M method; 271 String[] signature; 272 } 273 274 private final Class<?> mbeanInterface; 275 private final MBeanIntrospector<M> introspector; 276 private final MBeanInfo mbeanInfo; 277 private final Map<String, M> getters = newMap(); 278 private final Map<String, M> setters = newMap(); 279 private final Map<String, List<MethodAndSig>> ops = newMap(); 280} 281