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