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