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.axis2;
019    
020    import java.io.ByteArrayInputStream;
021    import java.io.PrintWriter;
022    import java.net.HttpURLConnection;
023    import java.net.URL;
024    import java.util.List;
025    
026    import javax.naming.Context;
027    import javax.servlet.ServletContext;
028    import javax.servlet.http.HttpServletRequest;
029    import javax.servlet.http.HttpServletResponse;
030    import javax.xml.ws.Binding;
031    import javax.xml.ws.WebServiceException;
032    import javax.xml.ws.handler.Handler;
033    
034    import org.apache.axiom.om.util.UUIDGenerator;
035    import org.apache.axis2.AxisFault;
036    import org.apache.axis2.Constants;
037    import org.apache.axis2.addressing.AddressingHelper;
038    import org.apache.axis2.addressing.EndpointReference;
039    import org.apache.axis2.context.ConfigurationContext;
040    import org.apache.axis2.context.ConfigurationContextFactory;
041    import org.apache.axis2.context.MessageContext;
042    import org.apache.axis2.context.OperationContext;
043    import org.apache.axis2.description.AxisService;
044    import org.apache.axis2.description.TransportInDescription;
045    import org.apache.axis2.description.TransportOutDescription;
046    import org.apache.axis2.engine.AxisEngine;
047    import org.apache.axis2.engine.Handler.InvocationResponse;
048    import org.apache.axis2.jaxws.binding.BindingImpl;
049    import org.apache.axis2.jaxws.binding.BindingUtils;
050    import org.apache.axis2.jaxws.description.EndpointDescription;
051    import org.apache.axis2.jaxws.description.impl.DescriptionUtils;
052    import org.apache.axis2.jaxws.description.xml.handler.HandlerChainType;
053    import org.apache.axis2.jaxws.description.xml.handler.HandlerChainsType;
054    import org.apache.axis2.jaxws.description.xml.handler.HandlerType;
055    import org.apache.axis2.jaxws.handler.lifecycle.factory.HandlerLifecycleManagerFactory;
056    import org.apache.axis2.jaxws.registry.FactoryRegistry;
057    import org.apache.axis2.jaxws.server.JAXWSMessageReceiver;
058    import org.apache.axis2.jaxws.server.endpoint.lifecycle.factory.EndpointLifecycleManagerFactory;
059    import org.apache.axis2.transport.OutTransportInfo;
060    import org.apache.axis2.transport.RequestResponseTransport;
061    import org.apache.axis2.transport.http.HTTPConstants;
062    import org.apache.axis2.transport.http.HTTPTransportReceiver;
063    import org.apache.axis2.transport.http.HTTPTransportUtils;
064    import org.apache.axis2.transport.http.TransportHeaders;
065    import org.apache.axis2.transport.http.util.RESTUtil;
066    import org.apache.axis2.util.MessageContextBuilder;
067    import org.apache.commons.logging.Log;
068    import org.apache.commons.logging.LogFactory;
069    import org.apache.geronimo.axis2.client.Axis2ConfigGBean;
070    import org.apache.geronimo.jaxws.JAXWSAnnotationProcessor;
071    import org.apache.geronimo.jaxws.JAXWSUtils;
072    import org.apache.geronimo.jaxws.JNDIResolver;
073    import org.apache.geronimo.jaxws.PortInfo;
074    import org.apache.geronimo.jaxws.ServerJNDIResolver;
075    import org.apache.geronimo.jaxws.annotations.AnnotationException;
076    import org.apache.geronimo.webservices.WebServiceContainer;
077    import org.apache.geronimo.webservices.saaj.SAAJUniverse;
078    
079    /**
080     * @version $Rev$ $Date$
081     */
082    public abstract class Axis2WebServiceContainer implements WebServiceContainer {
083    
084        private static final Log LOG = LogFactory.getLog(Axis2WebServiceContainer.class);
085    
086        public static final String REQUEST = Axis2WebServiceContainer.class.getName() + "@Request";
087        public static final String RESPONSE = Axis2WebServiceContainer.class.getName() + "@Response";
088    
089        private transient final ClassLoader classLoader;
090        
091        protected String endpointClassName;
092        protected org.apache.geronimo.jaxws.PortInfo portInfo;
093        protected ConfigurationContext configurationContext;
094        protected JNDIResolver jndiResolver;
095        protected Class endpointClass;
096        protected AxisService service;
097        protected URL configurationBaseUrl;
098        protected WSDLQueryHandler wsdlQueryHandler;
099        protected Binding binding;
100        protected JAXWSAnnotationProcessor annotationProcessor;
101        protected Context context;
102        protected GeronimoFactoryRegistry factoryRegistry;
103    
104        public Axis2WebServiceContainer(PortInfo portInfo,
105                                        String endpointClassName,
106                                        ClassLoader classLoader,
107                                        Context context,
108                                        URL configurationBaseUrl) {
109            this.classLoader = classLoader;
110            this.endpointClassName = endpointClassName;
111            this.portInfo = portInfo;
112            this.configurationBaseUrl = configurationBaseUrl;
113            this.context = context;
114            this.jndiResolver = new ServerJNDIResolver(context);
115        }
116    
117        public void init() throws Exception {
118            this.endpointClass = classLoader.loadClass(this.endpointClassName);
119            
120            Axis2ConfigGBean.registerClientConfigurationFactory();
121            
122            configurationContext = ConfigurationContextFactory.createBasicConfigurationContext("META-INF/geronimo-axis2.xml");
123    
124            // check to see if the wsdlLocation property is set in portInfo,
125            // if not checking if wsdlLocation exists in annotation
126            // if already set, annotation should not overwrite it.
127            if (portInfo.getWsdlFile() == null || portInfo.getWsdlFile().equals("")) {
128                // getwsdllocation from annotation if it exists
129                if (JAXWSUtils.containsWsdlLocation(this.endpointClass, classLoader)) {
130                    portInfo.setWsdlFile(JAXWSUtils.getServiceWsdlLocation(this.endpointClass, classLoader));
131                }
132            }
133    
134            AxisServiceGenerator serviceGen = createServiceGenerator();
135            if (portInfo.getWsdlFile() != null && !portInfo.getWsdlFile().equals("")) {
136                // WSDL file has been provided
137                service = serviceGen.getServiceFromWSDL(portInfo, endpointClass, configurationBaseUrl);
138            } else {
139                // No WSDL, let Axis2 handle it.
140                service = serviceGen.getServiceFromClass(this.endpointClass);
141            }
142    
143            service.setScope(Constants.SCOPE_APPLICATION);
144            configurationContext.getAxisConfiguration().addService(service);
145    
146            this.wsdlQueryHandler = new WSDLQueryHandler(this.service);
147            
148            /*
149             * This replaces HandlerLifecycleManagerFactory for all web services.
150             * This should be ok as we do our own handler instance managment and injection.
151             * Also, this does not affect service-ref clients, as we install our own
152             * HandlerResolver.
153             */        
154            FactoryRegistry.setFactory(HandlerLifecycleManagerFactory.class, 
155                                       new GeronimoHandlerLifecycleManagerFactory());
156    
157            FactoryRegistry.setFactory(EndpointLifecycleManagerFactory.class,
158                                       new GeronimoEndpointLifecycleManagerFactory());  
159        }  
160    
161        protected AxisServiceGenerator createServiceGenerator() {
162            return new AxisServiceGenerator();
163        }
164    
165        public void getWsdl(Request request, Response response) throws Exception {
166            doService(request, response);
167        }
168    
169        public void invoke(Request request, Response response) throws Exception {
170            // set factory registry
171            GeronimoFactoryRegistry oldRegistry = GeronimoFactoryRegistry.getGeronimoFactoryRegistry();
172            GeronimoFactoryRegistry.setGeronimoFactoryRegistry(this.factoryRegistry);
173            // set saaj universe
174            SAAJUniverse universe = new SAAJUniverse();
175            universe.set(SAAJUniverse.AXIS2);
176            try {
177                doService(request, response);
178            } finally {
179                // unset saaj universe 
180                universe.unset();
181                // unset factory registry
182                GeronimoFactoryRegistry.setGeronimoFactoryRegistry(oldRegistry);
183            }
184        }
185    
186        protected void doService(final Request request, final Response response)
187                throws Exception {        
188    
189            if (LOG.isDebugEnabled()) {
190                LOG.debug("Target URI: " + request.getURI());
191            }
192    
193            MessageContext msgContext = new MessageContext();
194            msgContext.setIncomingTransportName(Constants.TRANSPORT_HTTP);
195            msgContext.setProperty(MessageContext.REMOTE_ADDR, request.getRemoteAddr());
196    
197            try {
198                TransportOutDescription transportOut = this.configurationContext.getAxisConfiguration()
199                        .getTransportOut(Constants.TRANSPORT_HTTP);
200                TransportInDescription transportIn = this.configurationContext.getAxisConfiguration()
201                        .getTransportIn(Constants.TRANSPORT_HTTP);
202    
203                msgContext.setConfigurationContext(this.configurationContext);
204    
205                //TODO: Port this segment for session support.
206    //            String sessionKey = (String) this.httpcontext.getAttribute(HTTPConstants.COOKIE_STRING);
207    //            if (this.configurationContext.getAxisConfiguration().isManageTransportSession()) {
208    //                SessionContext sessionContext = this.sessionManager.getSessionContext(sessionKey);
209    //                msgContext.setSessionContext(sessionContext);
210    //            }
211                msgContext.setTransportIn(transportIn);
212                msgContext.setTransportOut(transportOut);
213                msgContext.setServiceGroupContextId(UUIDGenerator.getUUID());
214                msgContext.setServerSide(true);
215                msgContext.setAxisService(this.service);
216                
217                doService2(request, response, msgContext);
218            } catch (AxisFault e) {
219                LOG.debug(e.getMessage(), e);
220                handleFault(msgContext, response, e);
221            } catch (Throwable e) {            
222                String msg = "Exception occurred while trying to invoke service method doService()";
223                LOG.error(msg, e);
224                handleFault(msgContext, response, new AxisFault(msg, e));
225            }
226    
227        }
228    
229        private void handleFault(MessageContext msgContext, Response response, AxisFault e) {
230            // If the fault is not going along the back channel we should be 202ing
231            if (AddressingHelper.isFaultRedirected(msgContext)) {
232                response.setStatusCode(HttpURLConnection.HTTP_ACCEPTED);
233            } else {
234                response.setStatusCode(HttpURLConnection.HTTP_INTERNAL_ERROR);
235            }
236            
237            msgContext.setProperty(MessageContext.TRANSPORT_OUT, response.getOutputStream());
238            msgContext.setProperty(Constants.OUT_TRANSPORT_INFO, new Axis2TransportInfo(response));
239    
240            try {
241                MessageContext faultContext = MessageContextBuilder.createFaultMessageContext(msgContext, e);
242                AxisEngine.sendFault(faultContext);
243            } catch (Exception ex) {
244                LOG.warn("Error sending fault", ex);
245                if (!AddressingHelper.isFaultRedirected(msgContext)) {
246                    response.setStatusCode(HttpURLConnection.HTTP_INTERNAL_ERROR);
247                    response.setHeader(HTTPConstants.HEADER_CONTENT_TYPE, "text/plain");
248                    PrintWriter pw = new PrintWriter(response.getOutputStream());
249                    ex.printStackTrace(pw);
250                    pw.flush();
251                }
252            }
253        }
254        
255        protected String getServicePath(String contextRoot) {
256            String location = this.portInfo.getLocation();
257            if (location != null && location.startsWith(contextRoot)) {
258                return location.substring(contextRoot.length());
259            }
260            return null;
261        }
262        
263        public static String trimContext(String contextPath) {
264            if (contextPath != null) {
265                if (contextPath.startsWith("/")) {
266                    contextPath = contextPath.substring(1);
267                }
268                if (contextPath.endsWith("/")) {
269                    contextPath = contextPath.substring(0, contextPath.length() - 1);
270                }
271            }
272            return contextPath;
273        }
274        
275        public void doService2(Request request,
276                               Response response,
277                               MessageContext msgContext) throws Exception {
278                    
279            if (request.getMethod() == Request.GET) {
280                processGETRequest(request, response, this.service, msgContext);
281            } else if (request.getMethod() == Request.POST) {
282                processPOSTRequest(request, response, this.service, msgContext);
283            } else {
284                throw new UnsupportedOperationException("[" + request.getMethod() + " ] method not supported");
285            }
286    
287            // Finalize response
288            OperationContext operationContext = msgContext.getOperationContext();
289            Object contextWritten = null;
290            Object isTwoChannel = null;
291            if (operationContext != null) {
292                contextWritten = operationContext.getProperty(Constants.RESPONSE_WRITTEN);
293                isTwoChannel = operationContext.getProperty(Constants.DIFFERENT_EPR);
294            }
295    
296            if ((contextWritten != null) && Constants.VALUE_TRUE.equals(contextWritten)) {
297                if ((isTwoChannel != null) && Constants.VALUE_TRUE.equals(isTwoChannel)) {
298                    response.setStatusCode(HttpURLConnection.HTTP_ACCEPTED);
299                    return;
300                }
301                response.setStatusCode(HttpURLConnection.HTTP_OK);
302            } else {
303                response.setStatusCode(HttpURLConnection.HTTP_ACCEPTED);
304            }
305        }
306        
307        public void destroy() {
308        }
309            
310        public static class Axis2TransportInfo implements OutTransportInfo {
311            private Response response;
312    
313            public Axis2TransportInfo(Response response) {
314                this.response = response;
315            }
316    
317            public void setContentType(String contentType) {
318                response.setHeader(HTTPConstants.HEADER_CONTENT_TYPE, contentType);
319            }
320        }
321        
322        protected void processGETRequest(Request request, Response response, AxisService service, MessageContext msgContext) throws Exception{
323            String query = request.getURI().getQuery();
324            if (query != null &&
325                (query.startsWith("wsdl") || query.startsWith("WSDL") ||
326                 query.startsWith("xsd") || query.startsWith("XSD"))) {
327                // wsdl or xsd request
328    
329                if (portInfo.getWsdlFile() != null && !portInfo.getWsdlFile().equals("")) { 
330                    URL wsdlURL = AxisServiceGenerator.getWsdlURL(portInfo.getWsdlFile(),
331                                                                  configurationBaseUrl, 
332                                                                  classLoader);
333                    this.wsdlQueryHandler.writeResponse(request.getURI().toString(), 
334                                                        wsdlURL.toString(), 
335                                                        response.getOutputStream());
336                } else {
337                    throw new Exception("Service does not have WSDL");
338                }
339            } else if (AxisServiceGenerator.isSOAP11(service)) {
340                response.setContentType("text/html");
341                PrintWriter pw = new PrintWriter(response.getOutputStream());
342                pw.write("<html><title>Web Service</title><body>");
343                pw.write("Hi, this is '" + service.getName() + "' web service.");
344                pw.write("</body></html>");
345                pw.flush();
346            } else {            
347                // REST request
348                processURLRequest(request, response, service, msgContext);            
349            }
350        }
351    
352        protected void processPOSTRequest(Request request, Response response, AxisService service, MessageContext msgContext) throws Exception {
353            processXMLRequest(request, response, service, msgContext);
354        }
355        
356        protected void setMsgContextProperties(Request request, Response response, AxisService service, MessageContext msgContext) {
357            msgContext.setProperty(MessageContext.TRANSPORT_OUT, response.getOutputStream());
358            msgContext.setProperty(Constants.OUT_TRANSPORT_INFO, new Axis2TransportInfo(response));
359            msgContext.setProperty(RequestResponseTransport.TRANSPORT_CONTROL,
360                    new Axis2RequestResponseTransport(response));
361            msgContext.setProperty(Constants.Configuration.TRANSPORT_IN_URL, request.getURI().toString());
362            msgContext.setIncomingTransportName(Constants.TRANSPORT_HTTP);
363    
364            HttpServletRequest servletRequest =
365                (HttpServletRequest)request.getAttribute(WebServiceContainer.SERVLET_REQUEST);
366            msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST, servletRequest);
367    
368            HttpServletResponse servletResponse =
369                (HttpServletResponse)request.getAttribute(WebServiceContainer.SERVLET_RESPONSE);
370            msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETRESPONSE, servletResponse);
371    
372            ServletContext servletContext =
373                (ServletContext)request.getAttribute(WebServiceContainer.SERVLET_CONTEXT);
374            msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETCONTEXT, servletContext);    
375            
376            if (servletRequest != null) {
377                msgContext.setProperty(MessageContext.TRANSPORT_HEADERS, 
378                                       new TransportHeaders(servletRequest));
379            }
380            
381            if (this.binding != null) {
382                msgContext.setProperty(JAXWSMessageReceiver.PARAM_BINDING, this.binding);  
383            }
384            
385            msgContext.setTo(new EndpointReference(request.getURI().toString()));
386        }
387        
388        protected void processXMLRequest(Request request, 
389                                         Response response, 
390                                         AxisService service, 
391                                         MessageContext msgContext) throws Exception {
392            String contentType = request.getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
393            String soapAction = request.getHeader(HTTPConstants.HEADER_SOAP_ACTION);
394            if (soapAction == null) {
395                soapAction = "\"\"";
396            }
397            
398            ConfigurationContext configurationContext = msgContext.getConfigurationContext();
399            configurationContext.fillServiceContextAndServiceGroupContext(msgContext);
400    
401            setMsgContextProperties(request, response, service, msgContext);
402    
403            if (!HTTPTransportUtils.isRESTRequest(contentType)) {
404                HTTPTransportUtils.processHTTPPostRequest(msgContext,
405                                                          request.getInputStream(), 
406                                                          response.getOutputStream(), 
407                                                          contentType, 
408                                                          soapAction, 
409                                                          request.getURI().getPath());
410            } else {
411                RESTUtil.processXMLRequest(msgContext, 
412                                           request.getInputStream(),
413                                           response.getOutputStream(), 
414                                           contentType);
415            }
416        }
417        
418        protected void processURLRequest(Request request,
419                                         Response response,
420                                         AxisService service,
421                                         MessageContext msgContext) throws Exception {
422            String contentType = request.getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
423            
424            ConfigurationContext configurationContext = msgContext.getConfigurationContext();
425            configurationContext.fillServiceContextAndServiceGroupContext(msgContext);
426            
427            setMsgContextProperties(request, response, service, msgContext);
428                    
429            InvocationResponse processed = RESTUtil.processURLRequest(msgContext, 
430                                                                      response.getOutputStream(),
431                                                                      contentType);
432    
433            if (!processed.equals(InvocationResponse.CONTINUE)) {
434                response.setStatusCode(HttpURLConnection.HTTP_OK);
435                String s = HTTPTransportReceiver.getServicesHTML(configurationContext);
436                PrintWriter pw = new PrintWriter(response.getOutputStream());
437                pw.write(s);
438                pw.flush();
439            }
440        }    
441        
442        /*
443         * Gets the right handlers for the port/service/bindings and performs injection.
444         */
445        protected void configureHandlers() throws Exception {
446            EndpointDescription desc = AxisServiceGenerator.getEndpointDescription(this.service);
447            if (desc == null) {
448                this.binding = new BindingImpl("");
449            } else {
450                String xml = this.portInfo.getHandlersAsXML();
451                HandlerChainsType handlerChains = null;
452                if (xml != null) {
453                    ByteArrayInputStream in = new ByteArrayInputStream(xml.getBytes("UTF-8"));
454                    handlerChains = DescriptionUtils.loadHandlerChains(in);
455                    desc.setHandlerChain(handlerChains);
456                }
457                
458                if (LOG.isDebugEnabled()) {
459                    logHandlers(desc.getHandlerChain());
460                }
461                
462                this.binding = BindingUtils.createBinding(desc);
463                
464                DescriptionUtils.registerHandlerHeaders(desc.getAxisService(), this.binding.getHandlerChain());            
465            }
466        }
467    
468        private void logHandlers(HandlerChainsType handlerChains) {
469            if (handlerChains == null || handlerChains.getHandlerChain() == null
470                || handlerChains.getHandlerChain().isEmpty()) {
471                LOG.debug("No handlers");
472                return;
473            }
474    
475            for (HandlerChainType chains : handlerChains.getHandlerChain()) {
476                LOG.debug("Handler chain: " + chains.getServiceNamePattern() + " " + 
477                          chains.getPortNamePattern() + " " + chains.getProtocolBindings());
478                if (chains.getHandler() != null) {
479                    for (HandlerType chain : chains.getHandler()) {                    
480                        LOG.debug("  Handler: " + chain.getHandlerName().getValue() + " " + 
481                                  chain.getHandlerClass().getValue());
482                    }
483                }
484            }
485        }
486    
487        protected void injectHandlers() {
488            List<Handler> handlers = this.binding.getHandlerChain();
489            try {
490                for (Handler handler : handlers) {
491                    injectResources(handler);
492                }
493            } catch (AnnotationException e) {
494                throw new WebServiceException("Handler annotation failed", e);
495            }
496        }
497        
498        protected void destroyHandlers() {
499            if (this.annotationProcessor != null) {
500                // call handlers preDestroy
501                List<Handler> handlers = this.binding.getHandlerChain();
502                for (Handler handler : handlers) {
503                    this.annotationProcessor.invokePreDestroy(handler);
504                }
505            }
506        }
507        
508        protected void injectResources(Object instance) throws AnnotationException {
509            this.annotationProcessor.processAnnotations(instance);
510            this.annotationProcessor.invokePostConstruct(instance);
511        }
512        
513    }