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.kernel.basic;
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.gbean.GAttributeInfo;
026    import org.apache.geronimo.gbean.GBeanData;
027    import org.apache.geronimo.gbean.GBeanInfo;
028    import org.apache.geronimo.gbean.GOperationInfo;
029    import org.apache.geronimo.gbean.GOperationSignature;
030    import org.apache.geronimo.gbean.runtime.GBeanInstance;
031    import org.apache.geronimo.gbean.runtime.RawInvoker;
032    import org.apache.geronimo.kernel.Kernel;
033    import org.apache.geronimo.kernel.management.State;
034    import org.apache.geronimo.kernel.proxy.DeadProxyException;
035    import org.apache.geronimo.kernel.proxy.GeronimoManagedBean;
036    
037    import java.beans.Introspector;
038    import java.lang.reflect.Method;
039    import java.util.HashSet;
040    import java.util.Iterator;
041    import java.util.Map;
042    import java.util.Set;
043    
044    /**
045     * @version $Rev: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
046     */
047    public class ProxyMethodInterceptor implements MethodInterceptor {
048        /**
049         * Type of the proxy interface
050         */
051        private final Class proxyType;
052    
053        /**
054         * The object name to which we are connected.
055         */
056        private final AbstractName abstractName;
057    
058        /**
059         * GBeanInvokers keyed on the proxy interface method index
060         */
061        private ProxyInvoker[] gbeanInvokers;
062    
063        public ProxyMethodInterceptor(Class proxyType, Kernel kernel, AbstractName abstractName) {
064            assert proxyType != null;
065            assert kernel != null;
066            assert abstractName != null;
067    
068            this.proxyType = proxyType;
069            this.abstractName = abstractName;
070            gbeanInvokers = createGBeanInvokers(kernel, abstractName);
071        }
072    
073        public synchronized void destroy() {
074            gbeanInvokers = null;
075        }
076    
077        public AbstractName getAbstractName() {
078            return abstractName;
079        }
080    
081        public final Object intercept(final Object object, final Method method, final Object[] args, final MethodProxy proxy) throws Throwable {
082            ProxyInvoker gbeanInvoker;
083    
084            int interfaceIndex = proxy.getSuperIndex();
085            synchronized (this) {
086                if (gbeanInvokers == null) {
087                    throw new DeadProxyException("Proxy is no longer valid to gbean: " + abstractName);
088                }
089                gbeanInvoker = gbeanInvokers[interfaceIndex];
090            }
091    
092            if (gbeanInvoker == null) {
093                throw new UnsupportedOperationException("No implementation method: abstractName=" + abstractName + ", method=" + method);
094            }
095    
096            return gbeanInvoker.invoke(abstractName, args);
097        }
098    
099        private ProxyInvoker[] createGBeanInvokers(Kernel kernel, AbstractName abstractName) {
100            ProxyInvoker[] invokers;
101            try {
102                RawInvoker rawInvoker = (RawInvoker) kernel.getAttribute(abstractName, GBeanInstance.RAW_INVOKER);
103                invokers = createRawGBeanInvokers(rawInvoker, proxyType);
104            } catch (Exception e) {
105                invokers = createKernelGBeanInvokers(kernel, abstractName, proxyType);
106            }
107    
108            // handle equals, hashCode and toString directly here
109            try {
110                invokers[getSuperIndex(proxyType, proxyType.getMethod("equals", new Class[]{Object.class}))] = new EqualsInvoke(kernel);
111                invokers[getSuperIndex(proxyType, proxyType.getMethod("hashCode", null))] = new HashCodeInvoke();
112                invokers[getSuperIndex(proxyType, proxyType.getMethod("toString", null))] = new ToStringInvoke(proxyType.getName());
113                if(GeronimoManagedBean.class.isAssignableFrom(proxyType)) {
114                    invokers[getSuperIndex(proxyType, proxyType.getMethod("getState", null))] = new GetStateInvoke(kernel);
115                    invokers[getSuperIndex(proxyType, proxyType.getMethod("getStateInstance", null))] = new GetStateInstanceInvoke(kernel);
116                    invokers[getSuperIndex(proxyType, proxyType.getMethod("start", null))] = new StartInvoke(kernel);
117                    invokers[getSuperIndex(proxyType, proxyType.getMethod("startRecursive", null))] = new StartRecursiveInvoke(kernel);
118                    invokers[getSuperIndex(proxyType, proxyType.getMethod("stop", null))] = new StopInvoke(kernel);
119                    invokers[getSuperIndex(proxyType, proxyType.getMethod("getStartTime", null))] = new GetStartTimeInvoke(kernel);
120                    invokers[getSuperIndex(proxyType, proxyType.getMethod("getObjectName", null))] = new GetObjectNameInvoke(kernel);
121                }
122            } catch (Exception e) {
123                // this can not happen... all classes must implement equals, hashCode and toString
124                throw new AssertionError(new Exception("Could not install invoker for proxyType " + proxyType + " for target " + abstractName, e));
125            }
126    
127            return invokers;
128        }
129    
130        private ProxyInvoker[] createRawGBeanInvokers(RawInvoker rawInvoker, Class proxyType) {
131            Map operations = rawInvoker.getOperationIndex();
132            Map attributes = rawInvoker.getAttributeIndex();
133    
134            // build the method lookup table
135            FastClass fastClass = FastClass.create(proxyType);
136            ProxyInvoker[] invokers = new ProxyInvoker[fastClass.getMaxIndex() + 1];
137            Method[] methods = proxyType.getMethods();
138            for (int i = 0; i < methods.length; i++) {
139                Method method = methods[i];
140                int interfaceIndex = getSuperIndex(proxyType, method);
141                if (interfaceIndex >= 0) {
142                    invokers[interfaceIndex] = createRawGBeanInvoker(rawInvoker, method, operations, attributes);
143                }
144            }
145    
146            return invokers;
147        }
148    
149        private ProxyInvoker createRawGBeanInvoker(RawInvoker rawInvoker, Method method, Map operations, Map attributes) {
150            if (operations.containsKey(new GOperationSignature(method))) {
151                int methodIndex = ((Integer) operations.get(new GOperationSignature(method))).intValue();
152                return new RawOperationInvoker(rawInvoker, methodIndex);
153            }
154    
155            if (method.getName().startsWith("get")) {
156                String attributeName = method.getName().substring(3);
157                Integer methodIndex = ((Integer) attributes.get(attributeName));
158                if (methodIndex != null) {
159                    return new RawGetAttributeInvoker(rawInvoker, methodIndex.intValue());
160                }
161                methodIndex = getMethodIndex(attributes, attributeName);
162                if (methodIndex != null) {
163                    return new RawGetAttributeInvoker(rawInvoker, methodIndex.intValue());
164                }
165            }
166    
167            if (method.getName().startsWith("is")) {
168                String attributeName = method.getName().substring(2);
169                Integer methodIndex = ((Integer) attributes.get(attributeName));
170                if (methodIndex != null) {
171                    return new RawGetAttributeInvoker(rawInvoker, methodIndex.intValue());
172                }
173                methodIndex = getMethodIndex(attributes, attributeName);
174                if (methodIndex != null) {
175                    return new RawGetAttributeInvoker(rawInvoker, methodIndex.intValue());
176                }
177            }
178    
179            if (method.getName().startsWith("set")) {
180                String attributeName = method.getName().substring(3);
181                Integer methodIndex = ((Integer) attributes.get(attributeName));
182                if (methodIndex != null) {
183                    return new RawSetAttributeInvoker(rawInvoker, methodIndex.intValue());
184                }
185                methodIndex = getMethodIndex(attributes, attributeName);
186                if (methodIndex != null) {
187                    return new RawSetAttributeInvoker(rawInvoker, methodIndex.intValue());
188                }
189            }
190            return null;
191        }
192    
193        private ProxyInvoker[] createKernelGBeanInvokers(Kernel kernel, AbstractName abstractName, Class proxyType) {
194            GBeanInfo info;
195            try {
196                info = kernel.getGBeanInfo(abstractName);
197            } catch (Exception e) {
198                throw new IllegalArgumentException("Could not get GBeanInfo for target object: " + abstractName, e);
199            }
200    
201            // build attributeName->attributeInfo map
202            Set attributeInfos = info.getAttributes();
203            Set attributeNames = new HashSet(attributeInfos.size());
204            for (Iterator iterator = attributeInfos.iterator(); iterator.hasNext();) {
205                GAttributeInfo attributeInfo = (GAttributeInfo) iterator.next();
206                attributeNames.add(attributeInfo.getName());
207            }
208    
209            // build operationSignature->operationInfo map
210            Set operationInfos = info.getOperations();
211            Set operationSignatures = new HashSet(operationInfos.size());
212            for (Iterator iterator = operationInfos.iterator(); iterator.hasNext();) {
213                GOperationInfo operationInfo = (GOperationInfo) iterator.next();
214                operationSignatures.add(new GOperationSignature(operationInfo.getName(), operationInfo.getParameterList()));
215            }
216    
217            // build the method lookup table
218            FastClass fastClass = FastClass.create(proxyType);
219            ProxyInvoker[] invokers = new ProxyInvoker[fastClass.getMaxIndex() + 1];
220            Method[] methods = proxyType.getMethods();
221            for (int i = 0; i < methods.length; i++) {
222                Method method = methods[i];
223                int interfaceIndex = getSuperIndex(proxyType, method);
224                if (interfaceIndex >= 0) {
225                    invokers[interfaceIndex] = createJMXGBeanInvoker(kernel, method, operationSignatures, attributeNames);
226                }
227            }
228    
229            return invokers;
230        }
231    
232        private ProxyInvoker createJMXGBeanInvoker(Kernel kernel, Method method, Set operationSignatures, Set attributeNames) {
233            if (operationSignatures.contains(new GOperationSignature(method))) {
234                return new KernelOperationInvoker(kernel, method);
235            }
236    
237            String name = method.getName();
238            if (name.startsWith("get")) {
239                String attributeName = method.getName().substring(3);
240                if (attributeNames.contains(attributeName)) {
241                    return new KernelGetAttributeInvoker(kernel, attributeName);
242                }
243                attributeName = Introspector.decapitalize(attributeName);
244                if (attributeNames.contains(attributeName)) {
245                    return new KernelGetAttributeInvoker(kernel, attributeName);
246                }
247            } else if (name.startsWith("is")) {
248                String attrName = method.getName().substring(2);
249                if (attributeNames.contains(attrName)) {
250                    return new KernelGetAttributeInvoker(kernel, attrName);
251                }
252                attrName = Introspector.decapitalize(attrName);
253                if (attributeNames.contains(attrName)) {
254                    return new KernelGetAttributeInvoker(kernel, attrName);
255                }
256            } else if (name.startsWith("set")) {
257                String attrName = method.getName().substring(3);
258                if (attributeNames.contains(attrName)) {
259                    return new KernelSetAttributeInvoker(kernel, attrName);
260                }
261                attrName = Introspector.decapitalize(attrName);
262                if (attributeNames.contains(attrName)) {
263                    return new KernelSetAttributeInvoker(kernel, attrName);
264                }
265            }
266            return null;
267        }
268    
269        private static int getSuperIndex(Class proxyType, Method method) {
270            Signature signature = new Signature(method.getName(), Type.getReturnType(method), Type.getArgumentTypes(method));
271            MethodProxy methodProxy = MethodProxy.find(proxyType, signature);
272            if (methodProxy != null) {
273                return methodProxy.getSuperIndex();
274            }
275            return -1;
276        }
277    
278        private static Integer getMethodIndex(Map attributes, String attributeName) {
279            Iterator iter = attributes.keySet().iterator();
280            while (iter.hasNext()) {
281                String key = (String) iter.next();
282                if (key.equalsIgnoreCase(attributeName)) {
283                    return (Integer) attributes.get(key);
284                }
285            }
286            return null;
287        }
288    
289        static final class HashCodeInvoke implements ProxyInvoker {
290            public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable {
291                return new Integer(abstractName.hashCode());
292            }
293        }
294    
295        static final class EqualsInvoke implements ProxyInvoker {
296            private final Kernel kernel;
297    
298            public EqualsInvoke(Kernel kernel) {
299                this.kernel = kernel;
300            }
301    
302            public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable {
303                AbstractName proxyTarget = kernel.getAbstractNameFor(arguments[0]);
304                return Boolean.valueOf(abstractName.equals(proxyTarget));
305            }
306        }
307    
308        static final class ToStringInvoke implements ProxyInvoker {
309            private final String interfaceName;
310    
311            public ToStringInvoke(String interfaceName) {
312                this.interfaceName = "[" + interfaceName + ": ";
313            }
314    
315            public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable {
316                return interfaceName + abstractName + "]";
317            }
318        }
319    
320        static final class GetStateInvoke implements ProxyInvoker {
321            private Kernel kernel;
322    
323            public GetStateInvoke(Kernel kernel) {
324                this.kernel = kernel;
325            }
326    
327            public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable {
328                return new Integer(kernel.getGBeanState(abstractName));
329            }
330        }
331    
332        static final class GetStateInstanceInvoke implements ProxyInvoker {
333            private Kernel kernel;
334    
335            public GetStateInstanceInvoke(Kernel kernel) {
336                this.kernel = kernel;
337            }
338    
339            public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable {
340                return State.fromInt(kernel.getGBeanState(abstractName));
341            }
342        }
343    
344        static final class StartInvoke implements ProxyInvoker {
345            private Kernel kernel;
346    
347            public StartInvoke(Kernel kernel) {
348                this.kernel = kernel;
349            }
350    
351            public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable {
352                kernel.startGBean(abstractName);
353                return null;
354            }
355        }
356    
357        static final class StartRecursiveInvoke implements ProxyInvoker {
358            private Kernel kernel;
359    
360            public StartRecursiveInvoke(Kernel kernel) {
361                this.kernel = kernel;
362            }
363    
364            public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable {
365                kernel.startRecursiveGBean(abstractName);
366                return null;
367            }
368        }
369    
370        static final class GetStartTimeInvoke implements ProxyInvoker {
371            private Kernel kernel;
372    
373            public GetStartTimeInvoke(Kernel kernel) {
374                this.kernel = kernel;
375            }
376    
377            public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable {
378                return new Long(kernel.getGBeanStartTime(abstractName));
379            }
380        }
381    
382        static final class StopInvoke implements ProxyInvoker {
383            private Kernel kernel;
384    
385            public StopInvoke(Kernel kernel) {
386                this.kernel = kernel;
387            }
388    
389            public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable {
390                kernel.stopGBean(abstractName);
391                return null;
392            }
393        }
394    
395        static final class GetObjectNameInvoke implements ProxyInvoker {
396            private Kernel kernel;
397    
398            public GetObjectNameInvoke(Kernel kernel) {
399                this.kernel = kernel;
400            }
401    
402            public Object invoke(AbstractName abstractName, Object[] arguments) throws Throwable {
403                GBeanData gBeanData = kernel.getGBeanData(abstractName);
404                return gBeanData.getAbstractName().getObjectName().getCanonicalName();
405            }
406        }
407    }