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 java.lang.reflect.InvocationTargetException;
020    import java.util.ArrayList;
021    import java.util.Collections;
022    import java.util.List;
023    import java.util.Map;
024    import java.util.Set;
025    
026    import net.sf.cglib.proxy.Callback;
027    import net.sf.cglib.proxy.Enhancer;
028    import net.sf.cglib.proxy.MethodInterceptor;
029    import net.sf.cglib.reflect.FastClass;
030    import org.apache.commons.logging.Log;
031    import org.apache.commons.logging.LogFactory;
032    import org.apache.geronimo.gbean.AbstractName;
033    import org.apache.geronimo.gbean.GBeanInfo;
034    import org.apache.geronimo.kernel.ClassLoading;
035    import org.apache.geronimo.kernel.GBeanNotFoundException;
036    import org.apache.geronimo.kernel.Kernel;
037    import org.apache.geronimo.kernel.proxy.ProxyCreationException;
038    import org.apache.geronimo.kernel.proxy.ProxyFactory;
039    import org.apache.geronimo.kernel.proxy.ProxyManager;
040    
041    /**
042     * Creates proxies that communicate directly with a Kernel located in the same
043     * JVM as the client.
044     *
045     * @version $Rev:386515 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
046     */
047    public class BasicProxyManager implements ProxyManager {
048        private final static String MANAGED_BEAN_NAME = "org.apache.geronimo.kernel.proxy.GeronimoManagedBean";
049        private final static Log log = LogFactory.getLog(BasicProxyManager.class);
050        private final Kernel kernel;
051    
052        private final Map interceptors = Collections.synchronizedMap(new BasicProxyMap());
053    
054        public BasicProxyManager(Kernel kernel) {
055            this.kernel = kernel;
056        }
057    
058        public ProxyFactory createProxyFactory(Class[] types, ClassLoader classLoader) {
059            if (types == null) throw new NullPointerException("type is null");
060            if (types.length == 0) throw new IllegalArgumentException("interface list is empty");
061            if (classLoader == null) throw new NullPointerException("classLoader is null");
062    
063            Class managedBean = null;
064            try {
065                managedBean = classLoader.loadClass(MANAGED_BEAN_NAME);
066            } catch (ClassNotFoundException e) {
067                // Can't load GeronimoManagedBean if the incoming type doesn't have a ClassLoader set
068                log.debug("Unable to add GeronimoManagedBean to proxy (specified class loader does not have class)");
069            }
070    
071            if(managedBean != null) {
072                Class[] adjusted = new Class[types.length+1];
073                System.arraycopy(types, 0, adjusted, 0, types.length);
074                adjusted[types.length] = managedBean;
075                types = adjusted;
076            }
077    
078            return new ManagedProxyFactory(types, classLoader);
079        }
080    
081        public Object createProxy(AbstractName target, Class type) {
082            if (target == null) throw new NullPointerException("target is null");
083            if (type == null) throw new NullPointerException("type is null");
084    
085            try {
086                // if the type is visible from the target's classloader use it
087                // otherwise use the type's classloader
088                ClassLoader classLoader;
089                try {
090                    classLoader = kernel.getClassLoaderFor(target);
091                    if (!type.equals(ClassLoading.loadClass(type.getName(), classLoader))) {
092                        classLoader = type.getClassLoader();
093                    }
094                } catch (Exception ignored) {
095                    classLoader = type.getClassLoader();
096                }
097    
098                // add any interface exposed by the gbean that is visible from the selected class loader
099                List types = getVisibleInterfaces(target, classLoader, true);
100                if (types == null) types = new ArrayList();
101                types.add(type);
102    
103                return createProxyFactory((Class[]) types.toArray(new Class[types.size()]), classLoader).createProxy(target);
104            } catch (GBeanNotFoundException e) {
105                throw new IllegalArgumentException("Could not get GBeanInfo for target object: " + target, e);
106            }
107        }
108    
109        public Object createProxy(AbstractName target, ClassLoader classLoader) {
110            if (target == null) throw new NullPointerException("target is null");
111            if (classLoader == null) throw new NullPointerException("classLoader is null");
112    
113            try {
114                List types = getVisibleInterfaces(target, classLoader, true);
115                if (types == null) return null;
116                return createProxyFactory((Class[]) types.toArray(new Class[types.size()]), classLoader).createProxy(target);
117            } catch (GBeanNotFoundException e) {
118                throw new IllegalArgumentException("Could not get GBeanInfo for target object: " + target, e);
119            }
120        }
121    
122        private List getVisibleInterfaces(AbstractName target, ClassLoader classLoader, boolean shouldLog) throws GBeanNotFoundException {
123            GBeanInfo info = kernel.getGBeanInfo(target);
124            Set interfaces = info.getInterfaces();
125            if(interfaces.size() == 0) {
126                if (shouldLog) {
127                    log.warn("No interfaces found for " + target + " ("+target+")");
128                }
129                return null;
130            }
131            String[] names = (String[]) interfaces.toArray(new String[0]);
132            List types = new ArrayList();
133            for (int i = 0; i < names.length; i++) {
134                try {
135                    Class type = classLoader.loadClass(names[i]);
136                    if (type.isInterface()) {
137                        types.add(type);
138                    }
139                } catch (ClassNotFoundException e) {
140                    if (shouldLog) {
141                        log.warn("Could not load interface "+names[i]+" in provided ClassLoader for "+target);
142                    }
143                }
144            }
145            return types;
146        }
147    
148        public void destroyProxy(Object proxy) {
149            if (proxy == null) {
150                return;
151            }
152    
153            MethodInterceptor methodInterceptor = (MethodInterceptor) interceptors.remove(proxy);
154            if (methodInterceptor != null) {
155                doDestroy(methodInterceptor);
156            }
157        }
158    
159        public boolean isProxy(Object proxy) {
160            return interceptors.containsKey(proxy);
161        }
162    
163        public AbstractName getProxyTarget(Object proxy) {
164            MethodInterceptor methodInterceptor = (MethodInterceptor) interceptors.get(proxy);
165            if (methodInterceptor == null) {
166                return null;
167            }
168            return getAbstractName(methodInterceptor);
169        }
170    
171        private class ManagedProxyFactory implements ProxyFactory {
172            private final Class proxyType;
173            private final FastClass fastClass;
174    
175            public ManagedProxyFactory(Class type, ClassLoader classLoader) {
176                this(new Class[]{type}, classLoader);
177            }
178    
179            public ManagedProxyFactory(Class[] type, ClassLoader classLoader) {
180                Enhancer enhancer = new Enhancer();
181                if(type.length > 1) { // shrink first -- may reduce from many to one
182                    type = ClassLoading.reduceInterfaces(type);
183                }
184                if(type.length == 0) {
185                    throw new IllegalArgumentException("Cannot generate proxy for 0 interfaces!");
186                } else if(type.length == 1) { // Unlikely (as a result of GeronimoManagedBean)
187                    enhancer.setSuperclass(type[0]);
188                } else {
189                    if(type[0].isInterface()) {
190                        enhancer.setSuperclass(Object.class);
191                        enhancer.setInterfaces(type);
192                    } else { // there's a class and reduceInterfaces put the class in the first spot
193                        Class[] intfs = new Class[type.length-1];
194                        System.arraycopy(type, 1, intfs, 0, intfs.length);
195                        enhancer.setSuperclass(type[0]);
196                        enhancer.setInterfaces(intfs);
197                    }
198                }
199                enhancer.setClassLoader(classLoader);
200                enhancer.setCallbackType(MethodInterceptor.class);
201                enhancer.setUseFactory(false);
202                proxyType = enhancer.createClass();
203                fastClass = FastClass.create(proxyType);
204            }
205    
206            public Object createProxy(AbstractName target) {
207                assert target != null: "target is null";
208    
209                Callback callback = getMethodInterceptor(proxyType, kernel, target);
210    
211                Enhancer.registerCallbacks(proxyType, new Callback[]{callback});
212                try {
213                    Object proxy = fastClass.newInstance();
214                    interceptors.put(proxy, callback);
215                    return proxy;
216                } catch (InvocationTargetException e) {
217                    Throwable cause = e.getCause();
218                    if (cause instanceof RuntimeException) {
219                      throw (RuntimeException) cause;
220                    } else  if (cause instanceof Error) {
221                      throw (Error) cause;
222                    } else if (cause != null) {
223                      throw new ProxyCreationException(cause);
224                    } else {
225                      throw new ProxyCreationException(e);
226                    }
227                }
228            }
229        }
230    
231        protected Callback getMethodInterceptor(Class proxyType, Kernel kernel, AbstractName target) {
232            return new ProxyMethodInterceptor(proxyType, kernel, target);
233        }
234    
235        protected void doDestroy(MethodInterceptor methodInterceptor) {
236             ((ProxyMethodInterceptor)methodInterceptor).destroy();
237        }
238    
239        protected AbstractName getAbstractName(MethodInterceptor methodInterceptor) {
240            return ((ProxyMethodInterceptor)methodInterceptor).getAbstractName();
241        }
242    }