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