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    }