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    
018    package org.apache.geronimo.jaxws.client;
019    
020    import org.apache.geronimo.naming.reference.SimpleReference;
021    import org.apache.geronimo.naming.reference.ClassLoaderAwareReference;
022    import org.apache.geronimo.naming.reference.KernelAwareReference;
023    import org.apache.geronimo.kernel.Kernel;
024    import org.apache.geronimo.gbean.AbstractName;
025    import org.apache.commons.logging.Log;
026    import org.apache.commons.logging.LogFactory;
027    
028    import javax.naming.NamingException;
029    import javax.xml.ws.Service;
030    import javax.xml.ws.handler.HandlerResolver;
031    import javax.xml.namespace.QName;
032    import java.net.URL;
033    import java.net.MalformedURLException;
034    import java.net.URI;
035    import java.lang.reflect.InvocationTargetException;
036    import java.util.Map;
037    
038    import net.sf.cglib.proxy.Callback;
039    import net.sf.cglib.proxy.NoOp;
040    import net.sf.cglib.proxy.Enhancer;
041    import net.sf.cglib.proxy.MethodInterceptor;
042    import net.sf.cglib.reflect.FastConstructor;
043    import net.sf.cglib.reflect.FastClass;
044    
045    public abstract class JAXWSServiceReference extends SimpleReference implements ClassLoaderAwareReference, KernelAwareReference {
046        private static final Log LOG = LogFactory.getLog(JAXWSServiceReference.class);
047        private static final Class[] URL_SERVICE_NAME_CONSTRUCTOR =
048            new Class[] { URL.class, QName.class };
049        
050        protected String serviceClassName;
051        protected ClassLoader classLoader;
052        protected AbstractName moduleName;
053        protected URI wsdlURI;
054        protected QName serviceQName;
055        private Kernel kernel;
056        protected String handlerChainsXML;
057        protected Map<Object, EndpointInfo> seiInfoMap;
058        protected String referenceClassName;
059        
060        protected Class enhancedServiceClass;
061        protected Callback[] methodInterceptors;
062        protected FastConstructor serviceConstructor;
063    
064        public JAXWSServiceReference(String handlerChainsXML, Map<Object, EndpointInfo> seiInfoMap, AbstractName name, QName serviceQName, URI wsdlURI, String referenceClassName, String serviceClassName) {
065            this.handlerChainsXML = handlerChainsXML;
066            this.seiInfoMap = seiInfoMap;
067            this.moduleName = name;
068            this.serviceQName = serviceQName;
069            this.wsdlURI = wsdlURI;
070            this.referenceClassName = referenceClassName;
071            this.serviceClassName = serviceClassName;
072        }
073        
074        public void setClassLoader(ClassLoader classLoader) {
075            this.classLoader = classLoader;
076        }
077    
078        public void setKernel(Kernel kernel) {
079            this.kernel = kernel;
080        }
081    
082        private Class loadClass(String name) throws NamingException {
083            try {
084                return this.classLoader.loadClass(name);
085            } catch (ClassNotFoundException e) {
086                NamingException exception = new NamingException(
087                        "Count not load class " + name);
088                exception.initCause(e);
089                throw exception;
090            }
091        }
092    
093        private URL getWsdlURL() {
094            if (this.wsdlURI == null) {
095                return null;
096            }
097            try {
098                return new URL(this.wsdlURI.toString());
099            } catch (MalformedURLException e1) {
100                // not a URL, assume it's a local reference
101                try {
102                    URL moduleBaseUrl = (URL) this.kernel.getAttribute(
103                            this.moduleName, "configurationBaseUrl");
104                    return new URL(moduleBaseUrl.toString() + this.wsdlURI.toString());
105                } catch (Exception e) {
106                    URL wsdlURL = this.classLoader.getResource(this.wsdlURI.toString());
107                    if (wsdlURL == null) {
108                        LOG.warn("Error obtaining WSDL: " + this.wsdlURI, e);
109                    }
110                    return wsdlURL;
111                }
112            }
113        }
114    
115        private Class getReferenceClass() throws NamingException {
116            return (this.referenceClassName != null) ? loadClass(this.referenceClassName) : null;
117        }
118    
119        public Object getContent() throws NamingException {
120            Service instance = null;
121            URL wsdlURL = getWsdlURL();
122            
123            Class serviceClass = loadClass(this.serviceClassName);
124            Class referenceClass = getReferenceClass();              
125            
126            if (referenceClass != null && Service.class.isAssignableFrom(referenceClass)) {
127                serviceClass = referenceClass;
128            }
129                    
130            if (Service.class.equals(serviceClass)) {
131                serviceClass = GenericService.class;
132            }
133    
134            instance = createServiceProxy(serviceClass, this.classLoader, this.serviceQName, wsdlURL);
135    
136            HandlerResolver handlerResolver = getHandlerResolver(serviceClass);
137            if(handlerResolver != null) {
138                instance.setHandlerResolver(handlerResolver);
139            }
140    
141            if (referenceClass != null && !Service.class.isAssignableFrom(referenceClass)) {
142                // do port lookup
143                return instance.getPort(referenceClass);
144            } else {
145                // return service
146                return instance;
147            }
148        }
149    
150        protected abstract HandlerResolver getHandlerResolver(Class serviceClass);
151    
152        protected PortMethodInterceptor getPortMethodInterceptor() {
153            return new PortMethodInterceptor(this.seiInfoMap);
154        }
155        
156        private Service createServiceProxy(Class superClass, ClassLoader classLoader, QName serviceName, URL wsdlLocation) throws NamingException {
157            if (this.serviceConstructor == null) {            
158                // create method interceptors
159                Callback callback = getPortMethodInterceptor();
160                this.methodInterceptors = new Callback[] {NoOp.INSTANCE, callback};
161    
162                // create service class
163                Enhancer enhancer = new Enhancer();
164                enhancer.setClassLoader(classLoader);
165                enhancer.setSuperclass(superClass);
166                enhancer.setCallbackFilter(new PortMethodFilter());
167                enhancer.setCallbackTypes(new Class[] { NoOp.class, MethodInterceptor.class });
168                enhancer.setUseFactory(false);
169                enhancer.setUseCache(false);
170                this.enhancedServiceClass = enhancer.createClass(); 
171    
172                // get constructor
173                this.serviceConstructor = 
174                    FastClass.create(this.enhancedServiceClass).getConstructor(URL_SERVICE_NAME_CONSTRUCTOR);
175            }
176            
177            LOG.debug("Initializing service with: " + wsdlLocation + " " + serviceName);
178    
179            // associate the method interceptors with the generated service class on the current thread
180            Enhancer.registerCallbacks(this.enhancedServiceClass, this.methodInterceptors);
181            
182            Object[] arguments = new Object[] {wsdlLocation, serviceName};
183            
184            try {
185                return (Service)this.serviceConstructor.newInstance(arguments);
186            } catch (InvocationTargetException e) {
187                NamingException exception = new NamingException("Could not construct service proxy");
188                exception.initCause(e.getTargetException());
189                throw exception;
190            }
191        }
192    }