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: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
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 }