View Javadoc

1   /**
2    *
3    * Copyright 2004 The Apache Software Foundation
4    *
5    *  Licensed under the Apache License, Version 2.0 (the "License");
6    *  you may not use this file except in compliance with the License.
7    *  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   *  Unless required by applicable law or agreed to in writing, software
12   *  distributed under the License is distributed on an "AS IS" BASIS,
13   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   *  See the License for the specific language governing permissions and
15   *  limitations under the License.
16   */
17  package org.apache.geronimo.system.jmx;
18  
19  import net.sf.cglib.asm.Type;
20  import net.sf.cglib.core.Signature;
21  import net.sf.cglib.proxy.MethodInterceptor;
22  import net.sf.cglib.proxy.MethodProxy;
23  import net.sf.cglib.reflect.FastClass;
24  import org.apache.geronimo.gbean.AbstractName;
25  import org.apache.geronimo.kernel.Kernel;
26  import org.apache.geronimo.kernel.basic.KernelGetAttributeInvoker;
27  import org.apache.geronimo.kernel.basic.KernelOperationInvoker;
28  import org.apache.geronimo.kernel.basic.KernelSetAttributeInvoker;
29  import org.apache.geronimo.kernel.basic.ProxyInvoker;
30  import org.apache.geronimo.kernel.management.State;
31  import org.apache.geronimo.kernel.proxy.DeadProxyException;
32  import org.apache.geronimo.kernel.proxy.GeronimoManagedBean;
33  
34  import java.lang.reflect.Method;
35  import java.lang.reflect.Modifier;
36  
37  /**
38   * @version $Rev: 430508 $ $Date: 2006-08-10 12:56:47 -0700 (Thu, 10 Aug 2006) $
39   */
40  public class JMXProxyMethodInterceptor implements MethodInterceptor {
41      /**
42       * Type of the proxy interface
43       */
44      private final Class proxyType;
45  
46      /**
47       * The object name to which we are connected.
48       */
49      private final AbstractName objectName;
50  
51      /**
52       * GBeanInvokers keyed on the proxy interface method index
53       */
54      private ProxyInvoker[] gbeanInvokers;
55  
56      public JMXProxyMethodInterceptor(Class proxyType, Kernel kernel, AbstractName targetName) {
57          assert proxyType != null;
58          assert kernel != null;
59          assert targetName != null;
60  
61          this.proxyType = proxyType;
62          this.objectName = targetName;
63          gbeanInvokers = createGBeanInvokers(kernel);
64      }
65  
66      public synchronized void destroy() {
67          gbeanInvokers = null;
68      }
69  
70      public AbstractName getAbstractName() {
71          return objectName;
72      }
73  
74      public final Object intercept(final Object object, final Method method, final Object[] args, final MethodProxy proxy) throws Throwable {
75          ProxyInvoker gbeanInvoker;
76  
77          int interfaceIndex = proxy.getSuperIndex();
78          synchronized (this) {
79              if (gbeanInvokers == null) {
80                  throw new DeadProxyException("Proxy is no longer valid");
81              }
82              gbeanInvoker = gbeanInvokers[interfaceIndex];
83          }
84  
85          if (gbeanInvoker == null) {
86              throw new UnsupportedOperationException("No implementation method: objectName=" + objectName + ", method=" + method);
87          }
88  
89          return gbeanInvoker.invoke(objectName, args);
90      }
91  
92      private ProxyInvoker[] createGBeanInvokers(Kernel kernel) {
93          // build the method lookup table
94          FastClass fastClass = FastClass.create(proxyType);
95          ProxyInvoker[] invokers = new ProxyInvoker[fastClass.getMaxIndex() + 1];
96          Method[] methods = proxyType.getMethods();
97          for (int i = 0; i < methods.length; i++) {
98              Method method = methods[i];
99              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 }