001 /** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.geronimo.system.jmx; 018 019 import net.sf.cglib.asm.Type; 020 import net.sf.cglib.core.Signature; 021 import net.sf.cglib.proxy.MethodInterceptor; 022 import net.sf.cglib.proxy.MethodProxy; 023 import net.sf.cglib.reflect.FastClass; 024 import org.apache.geronimo.gbean.AbstractName; 025 import org.apache.geronimo.kernel.Kernel; 026 import org.apache.geronimo.kernel.basic.KernelGetAttributeInvoker; 027 import org.apache.geronimo.kernel.basic.KernelOperationInvoker; 028 import org.apache.geronimo.kernel.basic.KernelSetAttributeInvoker; 029 import org.apache.geronimo.kernel.basic.ProxyInvoker; 030 import org.apache.geronimo.kernel.management.State; 031 import org.apache.geronimo.kernel.proxy.DeadProxyException; 032 import org.apache.geronimo.kernel.proxy.GeronimoManagedBean; 033 034 import java.lang.reflect.Method; 035 import java.lang.reflect.Modifier; 036 037 /** 038 * @version $Rev: 476049 $ $Date: 2006-11-16 23:35:17 -0500 (Thu, 16 Nov 2006) $ 039 */ 040 public class JMXProxyMethodInterceptor implements MethodInterceptor { 041 /** 042 * Type of the proxy interface 043 */ 044 private final Class proxyType; 045 046 /** 047 * The object name to which we are connected. 048 */ 049 private final AbstractName objectName; 050 051 /** 052 * GBeanInvokers keyed on the proxy interface method index 053 */ 054 private ProxyInvoker[] gbeanInvokers; 055 056 public JMXProxyMethodInterceptor(Class proxyType, Kernel kernel, AbstractName targetName) { 057 assert proxyType != null; 058 assert kernel != null; 059 assert targetName != null; 060 061 this.proxyType = proxyType; 062 this.objectName = targetName; 063 gbeanInvokers = createGBeanInvokers(kernel); 064 } 065 066 public synchronized void destroy() { 067 gbeanInvokers = null; 068 } 069 070 public AbstractName getAbstractName() { 071 return objectName; 072 } 073 074 public final Object intercept(final Object object, final Method method, final Object[] args, final MethodProxy proxy) throws Throwable { 075 ProxyInvoker gbeanInvoker; 076 077 int interfaceIndex = proxy.getSuperIndex(); 078 synchronized (this) { 079 if (gbeanInvokers == null) { 080 throw new DeadProxyException("Proxy is no longer valid"); 081 } 082 gbeanInvoker = gbeanInvokers[interfaceIndex]; 083 } 084 085 if (gbeanInvoker == null) { 086 throw new UnsupportedOperationException("No implementation method: objectName=" + objectName + ", method=" + method); 087 } 088 089 return gbeanInvoker.invoke(objectName, args); 090 } 091 092 private ProxyInvoker[] createGBeanInvokers(Kernel kernel) { 093 // build the method lookup table 094 FastClass fastClass = FastClass.create(proxyType); 095 ProxyInvoker[] invokers = new ProxyInvoker[fastClass.getMaxIndex() + 1]; 096 Method[] methods = proxyType.getMethods(); 097 for (int i = 0; i < methods.length; i++) { 098 Method method = methods[i]; 099 int interfaceIndex = getSuperIndex(proxyType, method); 100 if (interfaceIndex >= 0) { 101 invokers[interfaceIndex] = createProxyInvoker(kernel, method); 102 } 103 } 104 105 // handle equals, hashCode and toString directly here 106 try { 107 invokers[getSuperIndex(proxyType, proxyType.getMethod("equals", new Class[]{Object.class}))] = new EqualsInvoke(kernel); 108 invokers[getSuperIndex(proxyType, proxyType.getMethod("hashCode", null))] = new HashCodeInvoke(); 109 invokers[getSuperIndex(proxyType, proxyType.getMethod("toString", null))] = new ToStringInvoke(proxyType.getName()); 110 if(GeronimoManagedBean.class.isAssignableFrom(proxyType)) { 111 invokers[getSuperIndex(proxyType, proxyType.getMethod("getState", null))] = new GetStateInvoke(kernel); 112 invokers[getSuperIndex(proxyType, proxyType.getMethod("getStateInstance", null))] = new GetStateInstanceInvoke(kernel); 113 invokers[getSuperIndex(proxyType, proxyType.getMethod("start", null))] = new StartInvoke(kernel); 114 invokers[getSuperIndex(proxyType, proxyType.getMethod("startRecursive", null))] = new StartRecursiveInvoke(kernel); 115 invokers[getSuperIndex(proxyType, proxyType.getMethod("stop", null))] = new StopInvoke(kernel); 116 invokers[getSuperIndex(proxyType, proxyType.getMethod("getStartTime", null))] = new GetStartTimeInvoke(kernel); 117 invokers[getSuperIndex(proxyType, proxyType.getMethod("getObjectName", null))] = new GetObjectNameInvoke(); 118 } 119 } catch (Exception e) { 120 // this can not happen... all classes must implement equals, hashCode and toString 121 throw new AssertionError(e); 122 } 123 124 return invokers; 125 } 126 127 private ProxyInvoker createProxyInvoker(Kernel kernel, Method method) { 128 String methodName = method.getName(); 129 if (!Modifier.isPublic(method.getModifiers()) || Modifier.isStatic(method.getModifiers())) { 130 return null; 131 } 132 133 // is this a getter is "is" method 134 if (method.getParameterTypes().length == 0 && method.getReturnType() != Void.TYPE) { 135 if (methodName.length() > 3 && methodName.startsWith("get") && !methodName.equals("getClass")) { 136 String propertyName = decapitalizePropertyName(methodName.substring(3)); 137 return new KernelGetAttributeInvoker(kernel, propertyName); 138 } else if (methodName.length() > 2 && methodName.startsWith("is")) { 139 String propertyName = decapitalizePropertyName(methodName.substring(2)); 140 return new KernelGetAttributeInvoker(kernel, propertyName); 141 } 142 } 143 144 // is this a setter method 145 if (method.getParameterTypes().length == 1 && 146 method.getReturnType() == Void.TYPE && 147 methodName.length() > 3 && 148 methodName.startsWith("set")) { 149 String propertyName = decapitalizePropertyName(methodName.substring(3)); 150 return new KernelSetAttributeInvoker(kernel, propertyName); 151 } 152 153 // it is just a plain old opertaion 154 return new KernelOperationInvoker(kernel, method); 155 } 156 157 private static int getSuperIndex(Class proxyType, Method method) { 158 Signature signature = new Signature(method.getName(), Type.getReturnType(method), Type.getArgumentTypes(method)); 159 MethodProxy methodProxy = MethodProxy.find(proxyType, signature); 160 if (methodProxy != null) { 161 return methodProxy.getSuperIndex(); 162 } 163 return -1; 164 } 165 166 private static String decapitalizePropertyName(String propertyName) { 167 if (Character.isUpperCase(propertyName.charAt(0))) { 168 return Character.toLowerCase(propertyName.charAt(0)) + propertyName.substring(1); 169 } 170 return propertyName; 171 } 172 173 static final class HashCodeInvoke implements ProxyInvoker { 174 public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable { 175 return new Integer(abstractName.hashCode()); 176 } 177 } 178 179 static final class EqualsInvoke implements ProxyInvoker { 180 private final Kernel kernel; 181 182 public EqualsInvoke(Kernel kernel) { 183 this.kernel = kernel; 184 } 185 186 public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable { 187 AbstractName proxyTarget = kernel.getAbstractNameFor(arguments[0]); 188 return Boolean.valueOf(abstractName.equals(proxyTarget)); 189 } 190 } 191 192 static final class ToStringInvoke implements ProxyInvoker { 193 private final String interfaceName; 194 195 public ToStringInvoke(String interfaceName) { 196 this.interfaceName = "[" + interfaceName + ": "; 197 } 198 199 public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable { 200 return interfaceName + abstractName + "]"; 201 } 202 } 203 204 static final class GetStateInvoke implements ProxyInvoker { 205 private Kernel kernel; 206 207 public GetStateInvoke(Kernel kernel) { 208 this.kernel = kernel; 209 } 210 211 public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable { 212 return new Integer(kernel.getGBeanState(abstractName)); 213 } 214 } 215 216 static final class GetStateInstanceInvoke implements ProxyInvoker { 217 private Kernel kernel; 218 219 public GetStateInstanceInvoke(Kernel kernel) { 220 this.kernel = kernel; 221 } 222 223 public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable { 224 return State.fromInt(kernel.getGBeanState(abstractName)); 225 } 226 } 227 228 static final class StartInvoke implements ProxyInvoker { 229 private Kernel kernel; 230 231 public StartInvoke(Kernel kernel) { 232 this.kernel = kernel; 233 } 234 235 public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable { 236 kernel.startGBean(abstractName); 237 return null; 238 } 239 } 240 241 static final class StartRecursiveInvoke implements ProxyInvoker { 242 private Kernel kernel; 243 244 public StartRecursiveInvoke(Kernel kernel) { 245 this.kernel = kernel; 246 } 247 248 public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable { 249 kernel.startRecursiveGBean(abstractName); 250 return null; 251 } 252 } 253 254 static final class GetStartTimeInvoke implements ProxyInvoker { 255 private Kernel kernel; 256 257 public GetStartTimeInvoke(Kernel kernel) { 258 this.kernel = kernel; 259 } 260 261 public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable { 262 return new Long(kernel.getGBeanStartTime(abstractName)); 263 } 264 } 265 266 static final class StopInvoke implements ProxyInvoker { 267 private Kernel kernel; 268 269 public StopInvoke(Kernel kernel) { 270 this.kernel = kernel; 271 } 272 273 public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable { 274 kernel.stopGBean(abstractName); 275 return null; 276 } 277 } 278 279 static final class GetObjectNameInvoke implements ProxyInvoker { 280 public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable { 281 return abstractName.getObjectName().getCanonicalName(); 282 } 283 } 284 }