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    package org.apache.geronimo.axis.server;
018    
019    import java.io.InputStream;
020    import java.io.IOException;
021    import java.net.URI;
022    import java.util.Iterator;
023    import java.util.Map;
024    import javax.servlet.http.HttpServletResponse;
025    import javax.wsdl.OperationType;
026    import javax.xml.soap.MimeHeader;
027    import javax.xml.soap.MimeHeaders;
028    import javax.xml.soap.SOAPMessage;
029    
030    import org.apache.axis.AxisEngine;
031    import org.apache.axis.AxisFault;
032    import org.apache.axis.Constants;
033    import org.apache.axis.Message;
034    import org.apache.axis.MessageContext;
035    import org.apache.axis.SOAPPart;
036    import org.apache.axis.handlers.soap.SOAPService;
037    import org.apache.axis.message.SOAPEnvelope;
038    import org.apache.axis.soap.SOAPConstants;
039    import org.apache.axis.transport.http.HTTPConstants;
040    import org.apache.axis.utils.Messages;
041    import org.apache.commons.logging.Log;
042    import org.apache.commons.logging.LogFactory;
043    import org.apache.geronimo.webservices.WebServiceContainer;
044    import org.apache.geronimo.webservices.saaj.SAAJUniverse;
045    import org.w3c.dom.Element;
046    
047    /**
048     * @version $Rev: 520432 $ $Date: 2007-03-20 11:35:27 -0400 (Tue, 20 Mar 2007) $
049     */
050    public class AxisWebServiceContainer implements WebServiceContainer {
051        public static final String REQUEST = AxisWebServiceContainer.class.getName() + "@Request";
052        public static final String RESPONSE = AxisWebServiceContainer.class.getName() + "@Response";
053    
054        private static Log log = LogFactory.getLog(AxisWebServiceContainer.class);
055    
056        public static final String XSD_NS = "http://www.w3.org/2001/XMLSchema";
057    
058        private final URI location;
059        private final URI wsdlLocation;
060        private final SOAPService service;
061    
062        private transient final ClassLoader classLoader;
063        private final Map wsdlMap;
064    
065        public AxisWebServiceContainer(URI location, URI wsdlURL, SOAPService service, Map wsdlMap, ClassLoader classLoader) {
066            this.location = location;
067            this.wsdlLocation = wsdlURL;
068            this.service = service;
069            this.wsdlMap = wsdlMap;
070            if (classLoader == null) {
071                this.classLoader = Thread.currentThread().getContextClassLoader();
072            } else {
073                this.classLoader = classLoader;
074            }
075        }
076    
077        public void invoke(Request req, Response res) throws Exception {
078            SAAJUniverse universe = new SAAJUniverse();
079            universe.set(SAAJUniverse.AXIS1);
080            try {
081                doService(req, res);
082            } finally {
083                universe.unset();
084            }
085        }
086        
087        protected void doService(Request req, Response res) throws Exception {
088            org.apache.axis.MessageContext messageContext = new org.apache.axis.MessageContext(null);
089            req.setAttribute(MESSAGE_CONTEXT, messageContext);
090    
091            messageContext.setClassLoader(classLoader);
092    
093            Message responseMessage = null;
094    
095            String contentType = req.getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
096            String contentLocation = req.getHeader(HTTPConstants.HEADER_CONTENT_LOCATION);
097            InputStream inputStream = req.getInputStream();
098            Message requestMessage = new Message(inputStream, false, contentType, contentLocation);
099    
100            messageContext.setRequestMessage(requestMessage);
101            messageContext.setProperty(HTTPConstants.MC_HTTP_SERVLETPATHINFO, req.getURI().getPath());
102            messageContext.setProperty(org.apache.axis.MessageContext.TRANS_URL, req.getURI().toString());
103            messageContext.setService(service);
104            messageContext.setProperty(REQUEST, req);
105            messageContext.setProperty(RESPONSE, res);
106            messageContext.setProperty(AxisEngine.PROP_DISABLE_PRETTY_XML, Boolean.TRUE);
107    
108            ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
109            try {
110                try {
111                    String characterEncoding = (String) requestMessage.getProperty(SOAPMessage.CHARACTER_SET_ENCODING);
112                    if (characterEncoding != null) {
113                        messageContext.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, characterEncoding);
114                    } else {
115                        messageContext.setProperty(SOAPMessage.CHARACTER_SET_ENCODING, "UTF-8");
116                    }
117    
118    
119                    String soapAction = req.getHeader(HTTPConstants.HEADER_SOAP_ACTION);
120                    if (soapAction != null) {
121                        messageContext.setUseSOAPAction(true);
122                        messageContext.setSOAPActionURI(soapAction);
123                    }
124    
125                    SOAPEnvelope env = requestMessage.getSOAPEnvelope();
126                    if (env != null && env.getSOAPConstants() != null) {
127                        messageContext.setSOAPConstants(env.getSOAPConstants());
128                    }
129                    SOAPService service = messageContext.getService();
130    
131                    Thread.currentThread().setContextClassLoader(classLoader);
132                    service.invoke(messageContext);
133    
134                    responseMessage = messageContext.getResponseMessage();
135                } catch (AxisFault fault) {
136                    
137                    if(req.getMethod() == req.GET && req.getParameters().isEmpty()){
138                            String serviceName = req.getURI().getRawPath();
139                        serviceName = serviceName.substring(serviceName.lastIndexOf("/")+1);
140                            printServiceInfo(res,serviceName);
141                            return;
142                    }else{
143                            responseMessage = handleFault(fault, res, messageContext);
144                    }
145    
146                } catch (Exception e) {
147                    responseMessage = handleException(messageContext, res, e);
148                }
149                //TODO investigate and fix operation == null!
150                if (messageContext.getOperation() != null) {
151                    if (messageContext.getOperation().getMep() == OperationType.ONE_WAY) {
152                        // No content, so just indicate accepted
153                        res.setStatusCode(202);
154                        return;
155                    } else if (responseMessage == null) {
156                        responseMessage = handleException(messageContext, null, new RuntimeException("No response for non-one-way operation"));
157                    }
158                } else if (responseMessage == null) {
159                    res.setStatusCode(202);
160                    return;
161                }
162                try {
163                    SOAPConstants soapConstants = messageContext.getSOAPConstants();
164                    String contentType1 = responseMessage.getContentType(soapConstants);
165                    res.setContentType(contentType1);
166                    // Transfer MIME headers to HTTP headers for response message.
167                    MimeHeaders responseMimeHeaders = responseMessage.getMimeHeaders();
168                    for (Iterator i = responseMimeHeaders.getAllHeaders(); i.hasNext();) {
169                        MimeHeader responseMimeHeader = (MimeHeader) i.next();
170                        res.setHeader(responseMimeHeader.getName(),
171                                responseMimeHeader.getValue());
172                    }
173                    //TODO discuss this with dims.
174    //                // synchronize the character encoding of request and response
175    //                String responseEncoding = (String) messageContext.getProperty(
176    //                        SOAPMessage.CHARACTER_SET_ENCODING);
177    //                if (responseEncoding != null) {
178    //                    try {
179    //                        responseMessage.setProperty(SOAPMessage.CHARACTER_SET_ENCODING,
180    //                                                responseEncoding);
181    //                    } catch (SOAPException e) {
182    //                        log.info(Messages.getMessage("exception00"), e);
183    //                    }
184    //                }
185                    //determine content type from message response
186                    contentType = responseMessage.getContentType(messageContext.
187                            getSOAPConstants());
188                    responseMessage.writeTo(res.getOutputStream());
189                } catch (Exception e) {
190                    log.warn(Messages.getMessage("exception00"), e);
191                }
192            } finally {
193                Thread.currentThread().setContextClassLoader(oldClassLoader);
194            }
195        }
196    
197        private Message handleException(MessageContext context, Response res, Exception e) {
198            Message responseMessage;
199            //other exceptions are internal trouble
200            responseMessage = context.getResponseMessage();
201            res.setStatusCode(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
202            Message responseMsg = responseMessage;
203            log.warn(Messages.getMessage("exception00"), e);
204            if (responseMsg == null) {
205                AxisFault fault = AxisFault.makeFault(e);
206                //log the fault
207                Element runtimeException = fault.lookupFaultDetail(Constants.QNAME_FAULTDETAIL_RUNTIMEEXCEPTION);
208                if (runtimeException != null) {
209                    log.debug(Messages.getMessage("axisFault00"), fault);
210                    //strip runtime details
211                    fault.removeFaultDetail(Constants.QNAME_FAULTDETAIL_RUNTIMEEXCEPTION);
212                }
213                responseMsg = new Message(fault);
214            }
215            responseMessage = responseMsg;
216            SOAPPart soapPart = (SOAPPart) responseMessage.getSOAPPart();
217            soapPart.getMessage().setMessageContext(context);
218            return responseMessage;
219        }
220    
221        private Message handleFault(AxisFault fault, Response res, MessageContext context) {
222            Message responseMessage;
223            Element runtimeException = fault.lookupFaultDetail(Constants.QNAME_FAULTDETAIL_RUNTIMEEXCEPTION);
224    
225            log.warn(Messages.getMessage("axisFault00"), fault);
226            if (runtimeException != null) {
227                //strip runtime details
228                fault.removeFaultDetail(Constants.QNAME_FAULTDETAIL_RUNTIMEEXCEPTION);
229            }
230    
231            int status = fault.getFaultCode().getLocalPart().startsWith("Server.Unauth")
232                    ? HttpServletResponse.SC_UNAUTHORIZED
233                    : HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
234            if (status == HttpServletResponse.SC_UNAUTHORIZED) {
235                // unauth access results in authentication request
236                // TODO: less generic realm choice?
237                res.setHeader("WWW-Authenticate", "Basic realm=\"AXIS\"");
238            }
239            res.setStatusCode(status);
240            responseMessage = context.getResponseMessage();
241            if (responseMessage == null) {
242                responseMessage = new Message(fault);
243                SOAPPart soapPart = (SOAPPart) responseMessage.getSOAPPart();
244                soapPart.getMessage().setMessageContext(context);
245            }
246            return responseMessage;
247        }
248    
249        public void getWsdl(Request request, Response response) throws Exception {
250            URI realLocation = request.getURI();
251    //        log.info("Request at " + realLocation);
252            String query = realLocation.getQuery();
253            if (query == null || !query.toLowerCase().startsWith("wsdl")) {
254                throw new IllegalStateException("request must contain a  wsdl or WSDL parameter: " + request.getParameters());
255            }
256            String locationKey;
257            if (query.length() > 4) {
258                locationKey = query.substring(5);
259            } else {
260                locationKey = wsdlLocation.toString();
261            }
262            Object wsdl = wsdlMap.get(locationKey);
263            if (wsdl == null) {
264                throw new IllegalStateException("No wsdl or schema known at location: " + locationKey);
265            }
266            URI updated = new URI(realLocation.getScheme(),
267                    realLocation.getUserInfo(),
268                    realLocation.getHost(),
269                    realLocation.getPort(),
270                    null, //try null for no path
271                    null,
272                    null);
273            String replaced = ((String) wsdl).replaceAll(LOCATION_REPLACEMENT_TOKEN, updated.toString());
274            response.getOutputStream().write(replaced.getBytes());
275            response.getOutputStream().flush();
276        }
277    
278        public void destroy() {
279        }
280    
281        public URI getLocation() {
282            return location;
283        }
284    
285        protected Object readResolve() {
286            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
287            if (classLoader == null) {
288                classLoader = AxisWebServiceContainer.class.getClassLoader();
289            }
290            return new AxisWebServiceContainer(location, wsdlLocation, service, wsdlMap, classLoader);
291        }
292    
293        /**
294         * print a snippet of service info.
295         * @param response response
296         * @param serviceName Name of the service
297         */
298    
299        private void printServiceInfo(Response response,String serviceName) throws IOException{
300            response.setContentType("text/html; charset=utf-8");
301            StringBuffer output = new StringBuffer("<h1>")
302                    .append(serviceName).append("</h1>\n");
303    
304            output.append("<p>").append(Messages.getMessage("axisService00"))
305                    .append("</p>\n");
306            output.append(
307                    "<i>").append(
308                    Messages.getMessage("perhaps00") ).append(
309                    "</i>\n");
310            response.getOutputStream().write(output.toString().getBytes());
311        }
312    
313    }
314    
315    
316    
317    
318    
319    
320    
321    
322    
323    
324    
325    
326    
327    
328    
329    
330    
331    
332    
333    
334    
335    
336    
337