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