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.corba.util;
018    
019    import net.sf.cglib.core.NamingPolicy;
020    import net.sf.cglib.core.Predicate;
021    import net.sf.cglib.proxy.Callback;
022    import net.sf.cglib.proxy.CallbackFilter;
023    import net.sf.cglib.proxy.Enhancer;
024    import net.sf.cglib.proxy.FixedValue;
025    import net.sf.cglib.proxy.MethodInterceptor;
026    import net.sf.cglib.proxy.NoOp;
027    import org.apache.commons.logging.Log;
028    import org.apache.commons.logging.LogFactory;
029    import org.apache.geronimo.gbean.GBeanLifecycle;
030    
031    import java.lang.reflect.Method;
032    import java.lang.reflect.Modifier;
033    
034    
035    /**
036     * @version $Revision: 706640 $ $Date: 2008-10-21 14:44:05 +0000 (Tue, 21 Oct 2008) $
037     */
038    public class DynamicStubClassLoader extends ClassLoader implements GBeanLifecycle {
039        private final static Log log = LogFactory.getLog(DynamicStubClassLoader.class);
040        private final static String PACKAGE_PREFIX = "org.omg.stub.";
041    
042        private boolean stopped = true;
043    
044        public synchronized Class loadClass(final String name) throws ClassNotFoundException {
045            if (stopped) {
046                throw new ClassNotFoundException("DynamicStubClassLoader is stopped");
047            }
048    
049            if (log.isDebugEnabled()) {
050                log.debug("Load class " + name);
051            }
052    
053            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
054    
055            // check if the stub already exists first
056            try {
057                return classLoader.loadClass(name);
058            } catch (ClassNotFoundException e) {
059                if (log.isDebugEnabled()) {
060                    log.debug("Unable to load class from the context class loader");
061                }
062            }
063    
064            // if this is not a class from the org.omb.stub name space don't attempt to generate
065            if (!name.startsWith(PACKAGE_PREFIX)) {
066                if (log.isDebugEnabled()) {
067                    log.debug("Could not load class: " + name);
068                }
069                throw new ClassNotFoundException("Could not load class: " + name);
070            }
071    
072            // load the interfaces class we are attempting to create a stub for
073            Class iface = loadStubInterfaceClass(name, classLoader);
074    
075            // create the stub builder
076            try {
077                Enhancer enhancer = new Enhancer();
078                enhancer.setSuperclass(ClientContextHolderStub.class);
079                enhancer.setInterfaces(new Class[]{iface});
080                enhancer.setCallbackFilter(FILTER);
081                enhancer.setCallbackTypes(new Class[]{NoOp.class, MethodInterceptor.class, FixedValue.class});
082                enhancer.setUseFactory(false);
083                enhancer.setClassLoader(classLoader);
084                enhancer.setNamingPolicy(new NamingPolicy() {
085                    public String getClassName(String s, String s1, Object o, Predicate predicate) {
086                        return name;
087                    }
088                });
089    
090                // generate the class
091                Class result = enhancer.createClass();
092                assert result != null;
093    
094                StubMethodInterceptor interceptor = new StubMethodInterceptor(iface);
095                Ids ids = new Ids(iface);
096                Enhancer.registerStaticCallbacks(result, new Callback[]{NoOp.INSTANCE, interceptor, ids});
097    
098                if (log.isDebugEnabled()) {
099                    log.debug("result: " + result.getName());
100                }
101                return result;
102            } catch (RuntimeException e) {
103                log.error("Unable to generate stub: " + name, e);
104                throw e;
105            } catch (Error e) {
106                log.error("Unable to generate stub: " + name, e);
107                throw e;
108            }
109        }
110    
111        private Class loadStubInterfaceClass(String name, ClassLoader classLoader) throws ClassNotFoundException {
112            try {
113                int begin = name.lastIndexOf('.') + 1;
114                String iPackage = name.substring(13, begin);
115                String iName = iPackage + name.substring(begin + 1, name.length() - 5);
116    
117                return classLoader.loadClass(iName);
118            } catch (ClassNotFoundException e) {
119                // don't log exceptions from CosNaming because it attempts to load every
120                // class bound into the name server
121                boolean shouldLog = true;
122                StackTraceElement[] stackTrace = e.getStackTrace();
123                for (int i = 0; i < stackTrace.length; i++) {
124                    StackTraceElement stackTraceElement = stackTrace[i];
125                    if (stackTraceElement.getClassName().equals("org.omg.CosNaming.NamingContextExtPOA") &&
126                            stackTraceElement.getMethodName().equals("_invoke")) {
127                        shouldLog = false;
128                        break;
129                    }
130                }
131                if (shouldLog) {
132                    log.error("Unable to generate stub", e);
133                }
134    
135                throw new ClassNotFoundException("Unable to generate stub", e);
136            }
137        }
138    
139        private static final CallbackFilter FILTER = new CallbackFilter() {
140            public int accept(Method method) {
141                // we don't intercept non-public methods like finalize
142                if (!Modifier.isPublic(method.getModifiers())) {
143                    return 0;
144                }
145    
146                if (method.getReturnType().equals(String[].class) && method.getParameterTypes().length == 0 && method.getName().equals("_ids")) {
147                    return 2;
148                }
149    
150                if (Modifier.isAbstract(method.getModifiers())) {
151                    return 1;
152                }
153    
154                return 0;
155            }
156        };
157    
158        private static final class Ids implements FixedValue {
159            private final String[] typeIds;
160    
161            public Ids(Class type) {
162                typeIds = Util.createCorbaIds(type);
163            }
164    
165            public Object loadObject() throws Exception {
166                return typeIds;
167            }
168        }
169    
170        public synchronized void doStart() throws Exception {
171            UtilDelegateImpl.setClassLoader(this);
172            stopped = false;
173        }
174    
175        public synchronized void doStop() throws Exception {
176            stopped = true;
177            log.debug("Stopped");
178        }
179    
180        public synchronized void doFail() {
181            stopped = true;
182            log.warn("Failed");
183        }
184    
185    }