0001: /*
0002: * Copyright 2001-2004 The Apache Software Foundation.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: /*
0018: * Modified by Nabh Information Systems, Inc. for Stringbeans Web Services
0019: * Framework.
0020: *
0021: * Modifications (c) 2005 Nabh Information Systems, Inc.
0022: */
0023: package com.nabhinc.ws.soap;
0024:
0025: import java.io.File;
0026: import java.io.IOException;
0027: import java.io.PrintWriter;
0028: import java.lang.reflect.InvocationTargetException;
0029: import java.lang.reflect.Method;
0030: import java.net.HttpURLConnection;
0031: import java.util.ArrayList;
0032: import java.util.Enumeration;
0033: import java.util.Iterator;
0034:
0035: import javax.servlet.ServletContext;
0036: import javax.servlet.ServletException;
0037: import javax.servlet.http.HttpServletRequest;
0038: import javax.servlet.http.HttpServletResponse;
0039: import javax.servlet.http.HttpUtils;
0040: import javax.xml.soap.MimeHeader;
0041: import javax.xml.soap.MimeHeaders;
0042: import javax.xml.soap.SOAPException;
0043: import javax.xml.soap.SOAPMessage;
0044:
0045: import org.apache.axis.AxisEngine;
0046: import org.apache.axis.AxisFault;
0047: import org.apache.axis.ConfigurationException;
0048: import org.apache.axis.Constants;
0049: import org.apache.axis.Handler;
0050: import org.apache.axis.Message;
0051: import org.apache.axis.MessageContext;
0052: import org.apache.axis.SimpleTargetedChain;
0053: import org.apache.axis.components.logger.LogFactory;
0054: import org.apache.axis.description.OperationDesc;
0055: import org.apache.axis.description.ServiceDesc;
0056: import org.apache.axis.handlers.soap.SOAPService;
0057: import org.apache.axis.management.ServiceAdmin;
0058: import org.apache.axis.security.servlet.ServletSecurityProvider;
0059: import org.apache.axis.transport.http.AxisHttpSession;
0060: import org.apache.axis.transport.http.AxisServletBase;
0061: import org.apache.axis.transport.http.FilterPrintWriter;
0062: import org.apache.axis.transport.http.HTTPConstants;
0063: import org.apache.axis.transport.http.HTTPTransport;
0064: import org.apache.axis.transport.http.ServletEndpointContextImpl;
0065: import org.apache.axis.utils.JavaUtils;
0066: import org.apache.axis.utils.Messages;
0067: import org.apache.axis.utils.XMLUtils;
0068: import org.apache.commons.logging.Log;
0069: import org.w3c.dom.Element;
0070:
0071: /**
0072: *
0073: * @author Doug Davis (dug@us.ibm.com)
0074: * @author Steve Loughran
0075: * xdoclet tags are not active yet; keep web.xml in sync.
0076: * To change the location of the services, change url-pattern in web.xml and
0077: * set parameter axis.servicesPath in server-config.wsdd. For more information see
0078: * <a href="http://ws.apache.org/axis/java/reference.html">Axis Reference Guide</a>.
0079: *
0080: * @web.servlet name="AxisServlet" display-name="Apache-Axis Servlet"
0081: * @web.servlet-mapping url-pattern="/servlet/AxisServlet"
0082: * @web.servlet-mapping url-pattern="*.jws"
0083: * @web.servlet-mapping url-pattern="/services/*"
0084: */
0085: public class AxisServlet extends AxisServletBase {
0086: protected static Log log = LogFactory.getLog(AxisServlet.class
0087: .getName());
0088:
0089: /**
0090: * this log is for timing
0091: */
0092: private static Log tlog = LogFactory
0093: .getLog(Constants.TIME_LOG_CATEGORY);
0094:
0095: /**
0096: * a separate log for exceptions lets users route them
0097: * differently from general low level debug info
0098: */
0099: private static Log exceptionLog = LogFactory
0100: .getLog(Constants.EXCEPTION_LOG_CATEGORY);
0101:
0102: public static final String INIT_PROPERTY_TRANSPORT_NAME = "transport.name";
0103:
0104: public static final String INIT_PROPERTY_USE_SECURITY = "use-servlet-security";
0105: public static final String INIT_PROPERTY_ENABLE_LIST = "axis.enableListQuery";
0106:
0107: public static final String INIT_PROPERTY_JWS_CLASS_DIR = "axis.jws.servletClassDir";
0108:
0109: // This will turn off the list of available services
0110: public static final String INIT_PROPERTY_DISABLE_SERVICES_LIST = "axis.disableServiceList";
0111:
0112: // Location of the services as defined by the servlet-mapping in web.xml
0113: public static final String INIT_PROPERTY_SERVICES_PATH = "axis.servicesPath";
0114:
0115: // These have default values.
0116: private String transportName;
0117:
0118: private Handler transport;
0119:
0120: private ServletSecurityProvider securityProvider = null;
0121:
0122: private String servicesPath;
0123:
0124: /**
0125: * cache of logging debug option; only evaluated at init time.
0126: * So no dynamic switching of logging options with this servlet.
0127: */
0128: private static boolean isDebug = false;
0129:
0130: /**
0131: * Should we enable the "?list" functionality on GETs? (off by
0132: * default because deployment information is a potential security
0133: * hole)
0134: */
0135: private boolean enableList = false;
0136:
0137: /**
0138: * Should we turn off the list of services when we receive a GET
0139: * at the servlet root?
0140: */
0141: private boolean disableServicesList = false;
0142:
0143: /**
0144: * Cached path to JWS output directory
0145: */
0146: private String jwsClassDir = null;
0147:
0148: protected String getJWSClassDir() {
0149: return jwsClassDir;
0150: }
0151:
0152: /**
0153: * create a new servlet instance
0154: */
0155: public AxisServlet() {
0156: }
0157:
0158: /**
0159: * Initialization method.
0160: */
0161: public void init() throws javax.servlet.ServletException {
0162: super .init();
0163: ServletContext context = getServletConfig().getServletContext();
0164:
0165: isDebug = log.isDebugEnabled();
0166: if (isDebug) {
0167: log.debug("In servlet init");
0168: }
0169: transportName = getOption(context,
0170: INIT_PROPERTY_TRANSPORT_NAME,
0171: HTTPTransport.DEFAULT_TRANSPORT_NAME);
0172:
0173: if (JavaUtils.isTrueExplicitly(getOption(context,
0174: INIT_PROPERTY_USE_SECURITY, null))) {
0175: securityProvider = new ServletSecurityProvider();
0176: }
0177:
0178: enableList = JavaUtils.isTrueExplicitly(getOption(context,
0179: INIT_PROPERTY_ENABLE_LIST, null));
0180:
0181: jwsClassDir = getOption(context, INIT_PROPERTY_JWS_CLASS_DIR,
0182: null);
0183:
0184: // Should we list services?
0185: disableServicesList = JavaUtils.isTrue(getOption(context,
0186: INIT_PROPERTY_DISABLE_SERVICES_LIST, "false"));
0187:
0188: servicesPath = getOption(context, INIT_PROPERTY_SERVICES_PATH,
0189: "/services/");
0190:
0191: /**
0192: * There are DEFINATE problems here if
0193: * getHomeDir and/or getDefaultJWSClassDir return null
0194: * (as they could with WebLogic).
0195: * This needs to be reexamined in the future, but this
0196: * should fix any NPE's in the mean time.
0197: */
0198: if (jwsClassDir != null) {
0199: if (getHomeDir() != null) {
0200: jwsClassDir = getHomeDir() + jwsClassDir;
0201: }
0202: } else {
0203: jwsClassDir = getDefaultJWSClassDir();
0204: }
0205:
0206: initQueryStringHandlers();
0207:
0208: // Setup the service admin
0209: try {
0210: ServiceAdmin.setEngine(this .getEngine(), context
0211: .getServerInfo());
0212: } catch (AxisFault af) {
0213: exceptionLog
0214: .info("Exception setting AxisEngine on ServiceAdmin "
0215: + af);
0216: }
0217: }
0218:
0219: /**
0220: * Process GET requests. This includes handoff of pseudo-SOAP requests
0221: *
0222: * @param request request in
0223: * @param response request out
0224: * @throws ServletException
0225: * @throws IOException
0226: */
0227: public void doGet(HttpServletRequest request,
0228: HttpServletResponse response) throws ServletException,
0229: IOException {
0230: if (isDebug) {
0231: log.debug("Enter: doGet()");
0232:
0233: }
0234: PrintWriter writer = new FilterPrintWriter(response);
0235:
0236: try {
0237: AxisEngine engine = getEngine();
0238: ServletContext servletContext = getServletConfig()
0239: .getServletContext();
0240:
0241: String pathInfo = request.getPathInfo();
0242: String realpath = servletContext.getRealPath(request
0243: .getServletPath());
0244: if (realpath == null) {
0245: realpath = request.getServletPath();
0246: }
0247:
0248: //JWS pages are special; they are the servlet path and there
0249: //is no pathinfo...we map the pathinfo to the servlet path to keep
0250: //it happy
0251: boolean isJWSPage = request.getRequestURI()
0252: .endsWith(".jws");
0253: if (isJWSPage) {
0254: pathInfo = request.getServletPath();
0255: }
0256:
0257: // Try to execute a query string plugin and return upon success.
0258:
0259: if (processQuery(request, response, writer) == true) {
0260: return;
0261: }
0262:
0263: boolean hasNoPath = (pathInfo == null || pathInfo
0264: .equals(""));
0265: if (!disableServicesList && hasNoPath) {
0266: // If the user requested the servlet (i.e. /axis/servlet/AxisServlet)
0267: // with no service name, present the user with a list of deployed
0268: // services to be helpful
0269: // Don't do this if has been turned off
0270: reportAvailableServices(response, writer, request);
0271: } else if (realpath != null) {
0272: // We have a pathname, so now we perform WSDL or list operations
0273:
0274: // get message context w/ various properties set
0275: MessageContext msgContext = createMessageContext(
0276: engine, request, response);
0277:
0278: // NOTE: HttpUtils.getRequestURL has been deprecated.
0279: // This line SHOULD be:
0280: // String url = req.getRequestURL().toString()
0281: // HOWEVER!!!! DON'T REPLACE IT! There's a bug in
0282: // req.getRequestURL that is not in HttpUtils.getRequestURL
0283: // req.getRequestURL returns "localhost" in the remote
0284: // scenario rather than the actual host name.
0285: //
0286: // But more importantly, getRequestURL() is a servlet 2.3
0287: // API and to support servlet 2.2 (aka WebSphere 4)
0288: // we need to leave this in for a while longer. tomj 10/14/2004
0289: //
0290: String url = HttpUtils.getRequestURL(request)
0291: .toString();
0292:
0293: msgContext.setProperty(MessageContext.TRANS_URL, url);
0294:
0295: // See if we can locate the desired service. If we
0296: // can't, return a 404 Not Found. Otherwise, just
0297: // print the placeholder message.
0298:
0299: String serviceName;
0300: if (pathInfo.startsWith("/")) {
0301: serviceName = pathInfo.substring(1);
0302: } else {
0303: serviceName = pathInfo;
0304: }
0305:
0306: SOAPService s = engine.getService(serviceName);
0307: if (s == null) {
0308: //no service: report it
0309: if (isJWSPage) {
0310: reportCantGetJWSService(request, response,
0311: writer);
0312: } else {
0313: reportCantGetAxisService(request, response,
0314: writer);
0315: }
0316:
0317: } else {
0318: //print a snippet of service info.
0319: reportServiceInfo(response, writer, s, serviceName);
0320: }
0321: } else {
0322: // We didn't have a real path in the request, so just
0323: // print a message informing the user that they reached
0324: // the servlet.
0325:
0326: response.setContentType("text/html; charset=utf-8");
0327: writer.println("<html><h1>Axis HTTP Servlet</h1>");
0328: writer.println(Messages.getMessage("reachedServlet00"));
0329:
0330: writer.println("<p>"
0331: + Messages.getMessage("transportName00", "<b>"
0332: + transportName + "</b>"));
0333: writer.println("</html>");
0334: }
0335: } catch (AxisFault fault) {
0336: reportTroubleInGet(fault, response, writer);
0337: } catch (Exception e) {
0338: reportTroubleInGet(e, response, writer);
0339: } finally {
0340: writer.close();
0341: if (isDebug) {
0342: log.debug("Exit: doGet()");
0343: }
0344: }
0345: }
0346:
0347: /**
0348: * when we get an exception or an axis fault in a GET, we handle
0349: * it almost identically: we go 'something went wrong', set the response
0350: * code to 500 and then dump info. But we dump different info for an axis fault
0351: * or subclass thereof.
0352: * @param exception what went wrong
0353: * @param response current response
0354: * @param writer open writer to response
0355: */
0356: private void reportTroubleInGet(Throwable exception,
0357: HttpServletResponse response, PrintWriter writer) {
0358: response.setContentType("text/html; charset=utf-8");
0359: response
0360: .setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
0361: writer.println("<h2>" + Messages.getMessage("error00")
0362: + "</h2>");
0363: writer.println("<p>" + Messages.getMessage("somethingWrong00")
0364: + "</p>");
0365: if (exception instanceof AxisFault) {
0366: AxisFault fault = (AxisFault) exception;
0367: processAxisFault(fault);
0368: writeFault(writer, fault);
0369: } else {
0370: logException(exception);
0371: writer.println("<pre>Exception - " + exception + "<br>");
0372: //dev systems only give fault dumps
0373: if (isDevelopment()) {
0374: writer.println(JavaUtils.stackToString(exception));
0375: }
0376: writer.println("</pre>");
0377: }
0378: }
0379:
0380: /**
0381: * routine called whenever an axis fault is caught; where they
0382: * are logged and any other business. The method may modify the fault
0383: * in the process
0384: * @param fault what went wrong.
0385: */
0386: protected void processAxisFault(AxisFault fault) {
0387: //log the fault
0388: Element runtimeException = fault
0389: .lookupFaultDetail(Constants.QNAME_FAULTDETAIL_RUNTIMEEXCEPTION);
0390: if (runtimeException != null) {
0391: exceptionLog
0392: .info(Messages.getMessage("axisFault00"), fault);
0393: //strip runtime details
0394: fault
0395: .removeFaultDetail(Constants.QNAME_FAULTDETAIL_RUNTIMEEXCEPTION);
0396: } else if (exceptionLog.isDebugEnabled()) {
0397: exceptionLog.debug(Messages.getMessage("axisFault00"),
0398: fault);
0399: }
0400: //dev systems only give fault dumps
0401: if (!isDevelopment()) {
0402: //strip out the stack trace
0403: fault
0404: .removeFaultDetail(Constants.QNAME_FAULTDETAIL_STACKTRACE);
0405: }
0406: }
0407:
0408: /**
0409: * log any exception to our output log, at our chosen level
0410: * @param e what went wrong
0411: */
0412: protected void logException(Throwable e) {
0413: exceptionLog.info(Messages.getMessage("exception00"), e);
0414: }
0415:
0416: /**
0417: * this method writes a fault out to an HTML stream. This includes
0418: * escaping the strings to defend against cross-site scripting attacks
0419: * @param writer
0420: * @param axisFault
0421: */
0422: private void writeFault(PrintWriter writer, AxisFault axisFault) {
0423: String localizedMessage = XMLUtils.xmlEncodeString(axisFault
0424: .getLocalizedMessage());
0425: writer.println("<pre>Fault - " + localizedMessage + "<br>");
0426: writer.println(axisFault.dumpToString());
0427: writer.println("</pre>");
0428: }
0429:
0430: /**
0431: * print a snippet of service info.
0432: * @param service service
0433: * @param writer output channel
0434: * @param serviceName where to put stuff
0435: */
0436:
0437: protected void reportServiceInfo(HttpServletResponse response,
0438: PrintWriter writer, SOAPService service, String serviceName) {
0439: response.setContentType("text/html; charset=utf-8");
0440:
0441: writer.println("<h1>" + service.getName() + "</h1>");
0442: writer.println("<p>" + Messages.getMessage("axisService00")
0443: + "</p>");
0444: writer.println("<i>" + Messages.getMessage("perhaps00")
0445: + "</i>");
0446: }
0447:
0448: /**
0449: * report that we have no WSDL
0450: *
0451: * This method was moved to the querystring handler QSWSDLHandler. The
0452: * method reportNoWSDL in AxisServlet is never called. Perhaps the method
0453: * is overwritten in subclasses of AxisServlet so the method wasn't
0454: * removed. See the discussion in
0455: *
0456: * http://nagoya.apache.org/bugzilla/show_bug.cgi?id=23845
0457: *
0458: * @param res
0459: * @param writer
0460: * @param moreDetailCode optional name of a message to provide more detail
0461: * @param axisFault optional fault string, for extra info at debug time only
0462: */
0463: protected void reportNoWSDL(HttpServletResponse res,
0464: PrintWriter writer, String moreDetailCode,
0465: AxisFault axisFault) {
0466: }
0467:
0468: /**
0469: * This method lists the available services; it is called when there is
0470: * nothing to execute on a GET
0471: * @param response
0472: * @param writer
0473: * @param request
0474: * @throws ConfigurationException
0475: * @throws AxisFault
0476: */
0477: protected void reportAvailableServices(
0478: HttpServletResponse response, PrintWriter writer,
0479: HttpServletRequest request) throws ConfigurationException,
0480: AxisFault {
0481: AxisEngine engine = getEngine();
0482:
0483: response.setContentType("text/html; charset=utf-8");
0484: writer.println("<h2>And now... Some Services</h2>");
0485:
0486: Iterator i;
0487: try {
0488: i = engine.getConfig().getDeployedServices();
0489: } catch (ConfigurationException configException) {
0490: //turn any internal configuration exceptions back into axis faults
0491: //if that is what they are
0492: if (configException.getContainedException() instanceof AxisFault) {
0493: throw (AxisFault) configException
0494: .getContainedException();
0495: } else {
0496: throw configException;
0497: }
0498: }
0499: // baseURL may change if <endpointURL> tag is used for
0500: // custom deployment at a different location
0501: String defaultBaseURL = getWebappBase(request) + servicesPath;
0502: writer.println("<ul>");
0503: while (i.hasNext()) {
0504: ServiceDesc sd = (ServiceDesc) i.next();
0505: StringBuffer sb = new StringBuffer();
0506: sb.append("<li>");
0507: String name = sd.getName();
0508: sb.append(name);
0509: sb.append(" <a href=\"");
0510: String endpointURL = sd.getEndpointURL();
0511: String baseURL = (endpointURL == null) ? defaultBaseURL
0512: : endpointURL;
0513: sb.append(baseURL);
0514: sb.append(name);
0515: sb.append("?wsdl\"><i>(wsdl)</i></a></li>");
0516: writer.println(sb.toString());
0517: ArrayList operations = sd.getOperations();
0518: if (!operations.isEmpty()) {
0519: writer.println("<ul>");
0520: for (Iterator it = operations.iterator(); it.hasNext();) {
0521: OperationDesc desc = (OperationDesc) it.next();
0522: writer.println("<li>" + desc.getName());
0523: }
0524: writer.println("</ul>");
0525: }
0526: }
0527: writer.println("</ul>");
0528: }
0529:
0530: /**
0531: * generate the error response to indicate that there is apparently no endpoint there
0532: * @param request the request that didnt have an edpoint
0533: * @param response response we are generating
0534: * @param writer open writer for the request
0535: */
0536: protected void reportCantGetAxisService(HttpServletRequest request,
0537: HttpServletResponse response, PrintWriter writer) {
0538: // no such service....
0539: response.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
0540: response.setContentType("text/html; charset=utf-8");
0541: writer.println("<h2>" + Messages.getMessage("error00")
0542: + "</h2>");
0543: writer.println("<p>" + Messages.getMessage("noService06")
0544: + "</p>");
0545: }
0546:
0547: /**
0548: * probe for a JWS page and report 'no service' if one is not found there
0549: * @param request the request that didnt have an edpoint
0550: * @param response response we are generating
0551: * @param writer open writer for the request
0552: */
0553: protected void reportCantGetJWSService(HttpServletRequest request,
0554: HttpServletResponse response, PrintWriter writer) {
0555: // first look to see if there is a service
0556: // requestPath is a work around to support serving .jws web services
0557: // from services URL - see AXIS-843 for more information
0558: String requestPath = request.getServletPath()
0559: + ((request.getPathInfo() != null) ? request
0560: .getPathInfo() : "");
0561: String realpath = getServletConfig().getServletContext()
0562: .getRealPath(requestPath);
0563: log.debug("JWS real path: " + realpath);
0564: boolean foundJWSFile = (new File(realpath).exists())
0565: && (realpath
0566: .endsWith(Constants.JWS_DEFAULT_FILE_EXTENSION));
0567: response.setContentType("text/html; charset=utf-8");
0568: if (foundJWSFile) {
0569: response.setStatus(HttpURLConnection.HTTP_OK);
0570: writer.println(Messages.getMessage("foundJWS00") + "<p>");
0571: String url = request.getRequestURI();
0572: String urltext = Messages.getMessage("foundJWS01");
0573: writer.println("<a href='" + url + "?wsdl'>" + urltext
0574: + "</a>");
0575: } else {
0576: response.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
0577: writer.println(Messages.getMessage("noService06"));
0578: }
0579: }
0580:
0581: /**
0582: * Process a POST to the servlet by handing it off to the Axis Engine.
0583: * Here is where SOAP messages are received
0584: * @param req posted request
0585: * @param res respose
0586: * @throws ServletException trouble
0587: * @throws IOException different trouble
0588: */
0589: public void doPost(HttpServletRequest req, HttpServletResponse res)
0590: throws ServletException, IOException {
0591: long t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;
0592: String soapAction = null;
0593: MessageContext msgContext = null;
0594: if (isDebug) {
0595: log.debug("Enter: doPost()");
0596: }
0597: if (tlog.isDebugEnabled()) {
0598: t0 = System.currentTimeMillis();
0599: }
0600:
0601: Message responseMsg = null;
0602: String contentType = null;
0603:
0604: try {
0605: AxisEngine engine = getEngine();
0606:
0607: if (engine == null) {
0608: // !!! should return a SOAP fault...
0609: ServletException se = new ServletException(Messages
0610: .getMessage("noEngine00"));
0611: log.debug("No Engine!", se);
0612: throw se;
0613: }
0614:
0615: res.setBufferSize(1024 * 8); // provide performance boost.
0616:
0617: /** get message context w/ various properties set
0618: */
0619: msgContext = createMessageContext(engine, req, res);
0620:
0621: // ? OK to move this to 'getMessageContext',
0622: // ? where it would also be picked up for 'doGet()' ?
0623: if (securityProvider != null) {
0624: if (isDebug) {
0625: log.debug("securityProvider:" + securityProvider);
0626: }
0627: msgContext.setProperty(
0628: MessageContext.SECURITY_PROVIDER,
0629: securityProvider);
0630: }
0631:
0632: /* Get request message
0633: */
0634: Message requestMsg = new Message(
0635: req.getInputStream(),
0636: false,
0637: req.getHeader(HTTPConstants.HEADER_CONTENT_TYPE),
0638: req
0639: .getHeader(HTTPConstants.HEADER_CONTENT_LOCATION));
0640: // Transfer HTTP headers to MIME headers for request message.
0641: MimeHeaders requestMimeHeaders = requestMsg
0642: .getMimeHeaders();
0643: for (Enumeration e = req.getHeaderNames(); e
0644: .hasMoreElements();) {
0645: String headerName = (String) e.nextElement();
0646: for (Enumeration f = req.getHeaders(headerName); f
0647: .hasMoreElements();) {
0648: String headerValue = (String) f.nextElement();
0649: requestMimeHeaders.addHeader(headerName,
0650: headerValue);
0651: }
0652: }
0653:
0654: if (isDebug) {
0655: log.debug("Request Message:" + requestMsg);
0656:
0657: /* Set the request(incoming) message field in the context */
0658: /**********************************************************/
0659: }
0660: msgContext.setRequestMessage(requestMsg);
0661: String url = HttpUtils.getRequestURL(req).toString();
0662: msgContext.setProperty(MessageContext.TRANS_URL, url);
0663: // put character encoding of request to message context
0664: // in order to reuse it during the whole process.
0665: String requestEncoding;
0666: try {
0667: requestEncoding = (String) requestMsg
0668: .getProperty(SOAPMessage.CHARACTER_SET_ENCODING);
0669: if (requestEncoding != null) {
0670: msgContext.setProperty(
0671: SOAPMessage.CHARACTER_SET_ENCODING,
0672: requestEncoding);
0673: }
0674: } catch (SOAPException e1) {
0675: }
0676:
0677: try {
0678: /**
0679: * Save the SOAPAction header in the MessageContext bag.
0680: * This will be used to tell the Axis Engine which service
0681: * is being invoked. This will save us the trouble of
0682: * having to parse the Request message - although we will
0683: * need to double-check later on that the SOAPAction header
0684: * does in fact match the URI in the body.
0685: */
0686: // (is this last stmt true??? (I don't think so - Glen))
0687: /********************************************************/
0688: soapAction = getSoapAction(req);
0689:
0690: if (soapAction != null) {
0691: msgContext.setUseSOAPAction(true);
0692: msgContext.setSOAPActionURI(soapAction);
0693: }
0694:
0695: // Create a Session wrapper for the HTTP session.
0696: // These can/should be pooled at some point.
0697: // (Sam is Watching! :-)
0698: msgContext.setSession(new AxisHttpSession(req));
0699:
0700: if (tlog.isDebugEnabled()) {
0701: t1 = System.currentTimeMillis();
0702: }
0703: /* Invoke the Axis engine... */
0704: /*****************************/
0705: if (isDebug) {
0706: log.debug("Invoking Axis Engine.");
0707: //here we run the message by the engine
0708: }
0709: engine.invoke(msgContext);
0710: if (isDebug) {
0711: log.debug("Return from Axis Engine.");
0712: }
0713: if (tlog.isDebugEnabled()) {
0714: t2 = System.currentTimeMillis();
0715: }
0716: responseMsg = msgContext.getResponseMessage();
0717:
0718: // We used to throw exceptions on null response messages.
0719: // They are actually OK in certain situations (asynchronous
0720: // services), so fall through here and return an ACCEPTED
0721: // status code below. Might want to install a configurable
0722: // error check for this later.
0723: } catch (AxisFault fault) {
0724: //log and sanitize
0725: processAxisFault(fault);
0726: configureResponseFromAxisFault(res, fault);
0727: responseMsg = msgContext.getResponseMessage();
0728: if (responseMsg == null) {
0729: responseMsg = new Message(fault);
0730: ((org.apache.axis.SOAPPart) responseMsg
0731: .getSOAPPart()).getMessage()
0732: .setMessageContext(msgContext);
0733: }
0734: } catch (Exception e) {
0735: //other exceptions are internal trouble
0736: responseMsg = msgContext.getResponseMessage();
0737: res
0738: .setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
0739: responseMsg = convertExceptionToAxisFault(e,
0740: responseMsg);
0741: ((org.apache.axis.SOAPPart) responseMsg.getSOAPPart())
0742: .getMessage().setMessageContext(msgContext);
0743: }
0744: } catch (AxisFault fault) {
0745: processAxisFault(fault);
0746: configureResponseFromAxisFault(res, fault);
0747: responseMsg = msgContext.getResponseMessage();
0748: if (responseMsg == null) {
0749: responseMsg = new Message(fault);
0750: ((org.apache.axis.SOAPPart) responseMsg.getSOAPPart())
0751: .getMessage().setMessageContext(msgContext);
0752: }
0753: }
0754:
0755: if (tlog.isDebugEnabled()) {
0756: t3 = System.currentTimeMillis();
0757: }
0758:
0759: /* Send response back along the wire... */
0760: /***********************************/
0761: if (responseMsg != null) {
0762: // Transfer MIME headers to HTTP headers for response message.
0763: MimeHeaders responseMimeHeaders = responseMsg
0764: .getMimeHeaders();
0765: for (Iterator i = responseMimeHeaders.getAllHeaders(); i
0766: .hasNext();) {
0767: MimeHeader responseMimeHeader = (MimeHeader) i.next();
0768: res.addHeader(responseMimeHeader.getName(),
0769: responseMimeHeader.getValue());
0770: }
0771: // synchronize the character encoding of request and response
0772: String responseEncoding = (String) msgContext
0773: .getProperty(SOAPMessage.CHARACTER_SET_ENCODING);
0774: if (responseEncoding != null) {
0775: try {
0776: responseMsg.setProperty(
0777: SOAPMessage.CHARACTER_SET_ENCODING,
0778: responseEncoding);
0779: } catch (SOAPException e) {
0780: }
0781: }
0782: //determine content type from message response
0783: contentType = responseMsg.getContentType(msgContext
0784: .getSOAPConstants());
0785: sendResponse(contentType, res, responseMsg);
0786: } else {
0787: // No content, so just indicate accepted
0788: res.setStatus(202);
0789: }
0790:
0791: // reset last Call object associated with the current thread
0792: //Service.clearCall();
0793:
0794: if (isDebug) {
0795: log.debug("Response sent.");
0796: log.debug("Exit: doPost()");
0797: }
0798: if (tlog.isDebugEnabled()) {
0799: t4 = System.currentTimeMillis();
0800: tlog.debug("axisServlet.doPost: "
0801: + soapAction
0802: + " pre="
0803: + (t1 - t0)
0804: + " invoke="
0805: + (t2 - t1)
0806: + " post="
0807: + (t3 - t2)
0808: + " send="
0809: + (t4 - t3)
0810: + " "
0811: + msgContext.getTargetService()
0812: + "."
0813: + ((msgContext.getOperation() == null) ? ""
0814: : msgContext.getOperation().getName()));
0815: }
0816:
0817: }
0818:
0819: /**
0820: * Configure the servlet response status code and maybe other headers
0821: * from the fault info.
0822: * @param response response to configure
0823: * @param fault what went wrong
0824: */
0825: protected void configureResponseFromAxisFault(
0826: HttpServletResponse response, AxisFault fault) {
0827: // then get the status code
0828: // It's been suggested that a lack of SOAPAction
0829: // should produce some other error code (in the 400s)...
0830: int status = getHttpServletResponseStatus(fault);
0831: if (status == HttpServletResponse.SC_UNAUTHORIZED) {
0832: // unauth access results in authentication request
0833: // TODO: less generic realm choice?
0834: response.setHeader("WWW-Authenticate",
0835: "Basic realm=\"AXIS\"");
0836: }
0837: response.setStatus(status);
0838: }
0839:
0840: /**
0841: * turn any Exception into an AxisFault, log it, set the response
0842: * status code according to what the specifications say and
0843: * return a response message for posting. This will be the response
0844: * message passed in if non-null; one generated from the fault otherwise.
0845: *
0846: * @param exception what went wrong
0847: * @param responseMsg what response we have (if any)
0848: * @return a response message to send to the user
0849: */
0850: protected Message convertExceptionToAxisFault(Throwable exception,
0851: Message responseMsg) {
0852: logException(exception);
0853: if (responseMsg == null) {
0854: AxisFault fault = null;
0855: try {
0856: fault = AxisFault.makeFault((Exception) exception);
0857: } catch (ClassCastException ex) {
0858: fault = AxisFault.makeFault(new Exception(exception
0859: .getMessage()));
0860: }
0861: processAxisFault(fault);
0862: responseMsg = new Message(fault);
0863: }
0864: return responseMsg;
0865: }
0866:
0867: /**
0868: * Extract information from AxisFault and map it to a HTTP Status code.
0869: *
0870: * @param af Axis Fault
0871: * @return HTTP Status code.
0872: */
0873: protected int getHttpServletResponseStatus(AxisFault af) {
0874: // TODO: Should really be doing this with explicit AxisFault
0875: // subclasses... --Glen
0876: return af.getFaultCode().getLocalPart().startsWith(
0877: "Server.Unauth") ? HttpServletResponse.SC_UNAUTHORIZED
0878: : HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
0879: // This will raise a 401 for both
0880: // "Unauthenticated" & "Unauthorized"...
0881: }
0882:
0883: /**
0884: * write a message to the response, set appropriate headers for content
0885: * type..etc.
0886: * @param res response
0887: * @param responseMsg message to write
0888: * @throws AxisFault
0889: * @throws IOException if the response stream can not be written to
0890: */
0891: protected void sendResponse(String contentType,
0892: HttpServletResponse res, Message responseMsg)
0893: throws AxisFault, IOException {
0894: if (responseMsg == null) {
0895: res.setStatus(HttpServletResponse.SC_NO_CONTENT);
0896: if (isDebug) {
0897: log.debug("NO AXIS MESSAGE TO RETURN!");
0898: //String resp = Messages.getMessage("noData00");
0899: //res.setContentLength((int) resp.getBytes().length);
0900: //res.getWriter().print(resp);
0901: }
0902: } else {
0903: if (isDebug) {
0904: log.debug("Returned Content-Type:" + contentType);
0905: // log.debug("Returned Content-Length:" +
0906: // responseMsg.getContentLength());
0907: }
0908:
0909: try {
0910: res.setContentType(contentType);
0911:
0912: /* My understand of Content-Length
0913: * HTTP 1.0
0914: * -Required for requests, but optional for responses.
0915: * HTTP 1.1
0916: * - Either Content-Length or HTTP Chunking is required.
0917: * Most servlet engines will do chunking if content-length is not specified.
0918: *
0919: *
0920: */
0921:
0922: //if(clientVersion == HTTPConstants.HEADER_PROTOCOL_V10) //do chunking if necessary.
0923: // res.setContentLength(responseMsg.getContentLength());
0924: responseMsg.writeTo(res.getOutputStream());
0925: } catch (SOAPException e) {
0926: logException(e);
0927: }
0928: }
0929:
0930: if (!res.isCommitted()) {
0931: res.flushBuffer(); // Force it right now.
0932: }
0933: }
0934:
0935: /**
0936: * Place the Request message in the MessagContext object - notice
0937: * that we just leave it as a 'ServletRequest' object and let the
0938: * Message processing routine convert it - we don't do it since we
0939: * don't know how it's going to be used - perhaps it might not
0940: * even need to be parsed.
0941: * @return a message context
0942: */
0943: protected MessageContext createMessageContext(AxisEngine engine,
0944: HttpServletRequest req, HttpServletResponse res) {
0945: MessageContext msgContext = new MessageContext(engine);
0946:
0947: String requestPath = getRequestPath(req);
0948:
0949: if (isDebug) {
0950: log.debug("MessageContext:" + msgContext);
0951: log.debug("HEADER_CONTENT_TYPE:"
0952: + req.getHeader(HTTPConstants.HEADER_CONTENT_TYPE));
0953: log
0954: .debug("HEADER_CONTENT_LOCATION:"
0955: + req
0956: .getHeader(HTTPConstants.HEADER_CONTENT_LOCATION));
0957: log.debug("Constants.MC_HOME_DIR:"
0958: + String.valueOf(getHomeDir()));
0959: log.debug("Constants.MC_RELATIVE_PATH:" + requestPath);
0960: log.debug("HTTPConstants.MC_HTTP_SERVLETLOCATION:"
0961: + String.valueOf(getWebInfPath()));
0962: log.debug("HTTPConstants.MC_HTTP_SERVLETPATHINFO:"
0963: + req.getPathInfo());
0964: log
0965: .debug("HTTPConstants.HEADER_AUTHORIZATION:"
0966: + req
0967: .getHeader(HTTPConstants.HEADER_AUTHORIZATION));
0968: log
0969: .debug("Constants.MC_REMOTE_ADDR:"
0970: + req.getRemoteAddr());
0971: log.debug("configPath:" + String.valueOf(getWebInfPath()));
0972: }
0973:
0974: /* Set the Transport */
0975: /*********************/
0976: msgContext.setTransportName(transportName);
0977:
0978: /* Save some HTTP specific info in the bag in case someone needs it */
0979: /********************************************************************/
0980: msgContext.setProperty(Constants.MC_JWS_CLASSDIR, jwsClassDir);
0981: msgContext.setProperty(Constants.MC_HOME_DIR, getHomeDir());
0982: msgContext.setProperty(Constants.MC_RELATIVE_PATH, requestPath);
0983: msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLET, this );
0984: msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST,
0985: req);
0986: msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETRESPONSE,
0987: res);
0988: msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETLOCATION,
0989: getWebInfPath());
0990: msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETPATHINFO,
0991: req.getPathInfo());
0992: msgContext.setProperty(HTTPConstants.HEADER_AUTHORIZATION, req
0993: .getHeader(HTTPConstants.HEADER_AUTHORIZATION));
0994: msgContext.setProperty(Constants.MC_REMOTE_ADDR, req
0995: .getRemoteAddr());
0996:
0997: // Set up a javax.xml.rpc.server.ServletEndpointContext
0998: ServletEndpointContextImpl sec = new ServletEndpointContextImpl();
0999:
1000: msgContext.setProperty(Constants.MC_SERVLET_ENDPOINT_CONTEXT,
1001: sec);
1002: /* Save the real path */
1003: /**********************/
1004: String realpath = getServletConfig().getServletContext()
1005: .getRealPath(requestPath);
1006:
1007: if (realpath != null) {
1008: msgContext.setProperty(Constants.MC_REALPATH, realpath);
1009: }
1010:
1011: msgContext
1012: .setProperty(Constants.MC_CONFIGPATH, getWebInfPath());
1013:
1014: return msgContext;
1015: }
1016:
1017: /**
1018: * Extract the SOAPAction header.
1019: * if SOAPAction is null then we'll we be forced to scan the body for it.
1020: * if SOAPAction is "" then use the URL
1021: * @param req incoming request
1022: * @return the action
1023: * @throws AxisFault
1024: */
1025: protected String getSoapAction(HttpServletRequest req)
1026: throws AxisFault {
1027: String soapAction = req
1028: .getHeader(HTTPConstants.HEADER_SOAP_ACTION);
1029:
1030: if (isDebug) {
1031: log.debug("HEADER_SOAP_ACTION:" + soapAction);
1032:
1033: /**
1034: * Technically, if we don't find this header, we should probably fault.
1035: * It's required in the SOAP HTTP binding.
1036: */
1037: }
1038: if (soapAction == null) {
1039: AxisFault af = new AxisFault("Client.NoSOAPAction",
1040: Messages.getMessage("noHeader00", "SOAPAction"),
1041: null, null);
1042:
1043: exceptionLog.error(Messages.getMessage("genFault00"), af);
1044:
1045: throw af;
1046: }
1047: // the SOAP 1.1 spec & WS-I 1.0 says:
1048: // soapaction = "SOAPAction" ":" [ <"> URI-reference <"> ]
1049: // some implementations leave off the quotes
1050: // we strip them if they are present
1051: if (soapAction.startsWith("\"") && soapAction.endsWith("\"")
1052: && soapAction.length() >= 2) {
1053: int end = soapAction.length() - 1;
1054: soapAction = soapAction.substring(1, end);
1055: }
1056:
1057: if (soapAction.length() == 0) {
1058: soapAction = req.getContextPath(); // Is this right?
1059:
1060: }
1061: return soapAction;
1062: }
1063:
1064: /**
1065: * Provided to allow overload of default JWSClassDir
1066: * by derived class.
1067: * @return directory for JWS files
1068: */
1069: protected String getDefaultJWSClassDir() {
1070: return (getWebInfPath() == null) ? null // ??? what is a good FINAL default for WebLogic?
1071: : getWebInfPath() + File.separator + "jwsClasses";
1072: }
1073:
1074: /**
1075: * Initialize a Handler for the transport defined in the Axis server config.
1076: * This includes optionally filling in query string handlers.
1077: */
1078:
1079: public void initQueryStringHandlers() {
1080: try {
1081: this .transport = getEngine().getTransport(
1082: this .transportName);
1083:
1084: if (this .transport == null) {
1085: // No transport by this name is defined. Therefore, fill in default
1086: // query string handlers.
1087:
1088: this .transport = new SimpleTargetedChain();
1089:
1090: this .transport.setOption("qs.list",
1091: "org.apache.axis.transport.http.QSListHandler");
1092: this .transport
1093: .setOption("qs.method",
1094: "org.apache.axis.transport.http.QSMethodHandler");
1095: this .transport.setOption("qs.wsdl",
1096: "org.apache.axis.transport.http.QSWSDLHandler");
1097:
1098: return;
1099: }
1100:
1101: else {
1102: // See if we should use the default query string handlers.
1103: // By default, set this to true (for backwards compatibility).
1104:
1105: boolean defaultQueryStrings = true;
1106: String useDefaults = (String) this .transport
1107: .getOption("useDefaultQueryStrings");
1108:
1109: if ((useDefaults != null)
1110: && useDefaults.toLowerCase().equals("false")) {
1111: defaultQueryStrings = false;
1112: }
1113:
1114: if (defaultQueryStrings == true) {
1115: // We should use defaults, so fill them in.
1116:
1117: this .transport
1118: .setOption("qs.list",
1119: "org.apache.axis.transport.http.QSListHandler");
1120: this .transport
1121: .setOption("qs.method",
1122: "org.apache.axis.transport.http.QSMethodHandler");
1123: this .transport
1124: .setOption("qs.wsdl",
1125: "org.apache.axis.transport.http.QSWSDLHandler");
1126: }
1127: }
1128: }
1129:
1130: catch (AxisFault e) {
1131: // Some sort of problem occurred, let's just make a default transport.
1132:
1133: this .transport = new SimpleTargetedChain();
1134:
1135: this .transport.setOption("qs.list",
1136: "org.apache.axis.transport.http.QSListHandler");
1137: this .transport.setOption("qs.method",
1138: "org.apache.axis.transport.http.QSMethodHandler");
1139: this .transport.setOption("qs.wsdl",
1140: "org.apache.axis.transport.http.QSWSDLHandler");
1141:
1142: return;
1143: }
1144: }
1145:
1146: /**
1147: * Attempts to invoke a plugin for the query string supplied in the URL.
1148: *
1149: * @param request the servlet's HttpServletRequest object.
1150: * @param response the servlet's HttpServletResponse object.
1151: * @param writer the servlet's PrintWriter object.
1152: */
1153:
1154: private boolean processQuery(HttpServletRequest request,
1155: HttpServletResponse response, PrintWriter writer)
1156: throws AxisFault {
1157: // Attempt to instantiate a plug-in handler class for the query string
1158: // handler classes defined in the HTTP transport.
1159:
1160: String path = request.getServletPath();
1161: String queryString = request.getQueryString();
1162: String serviceName;
1163: AxisEngine engine = getEngine();
1164: Iterator i = this .transport.getOptions().keySet().iterator();
1165:
1166: if (queryString == null) {
1167: return false;
1168: }
1169:
1170: String servletURI = request.getContextPath() + path;
1171: String reqURI = request.getRequestURI();
1172: // chop off '/'.
1173: if (servletURI.length() + 1 < reqURI.length()) {
1174: serviceName = reqURI.substring(servletURI.length() + 1);
1175: } else {
1176: serviceName = "";
1177: }
1178: while (i.hasNext() == true) {
1179: String queryHandler = (String) i.next();
1180:
1181: if (queryHandler.startsWith("qs.") == true) {
1182: // Only attempt to match the query string with transport
1183: // parameters prefixed with "qs:".
1184:
1185: String handlerName = queryHandler.substring(
1186: queryHandler.indexOf(".") + 1).toLowerCase();
1187:
1188: // Determine the name of the plugin to invoke by using all text
1189: // in the query string up to the first occurence of &, =, or the
1190: // whole string if neither is present.
1191:
1192: int length = 0;
1193: boolean firstParamFound = false;
1194:
1195: while (firstParamFound == false
1196: && length < queryString.length()) {
1197: char ch = queryString.charAt(length++);
1198:
1199: if (ch == '&' || ch == '=') {
1200: firstParamFound = true;
1201:
1202: --length;
1203: }
1204: }
1205:
1206: if (length < queryString.length()) {
1207: queryString = queryString.substring(0, length);
1208: }
1209:
1210: if (queryString.toLowerCase().equals(handlerName) == true) {
1211: // Query string matches a defined query string handler name.
1212:
1213: // If the defined class name for this query string handler is blank,
1214: // just return (the handler is "turned off" in effect).
1215:
1216: if (this .transport.getOption(queryHandler).equals(
1217: "")) {
1218: return false;
1219: }
1220:
1221: try {
1222: // Attempt to dynamically load the query string handler
1223: // and its "invoke" method.
1224:
1225: MessageContext msgContext = createMessageContext(
1226: engine, request, response);
1227: Class plugin = Class
1228: .forName((String) this .transport
1229: .getOption(queryHandler));
1230: Method pluginMethod = plugin.getDeclaredMethod(
1231: "invoke", new Class[] { msgContext
1232: .getClass() });
1233: String url = HttpUtils.getRequestURL(request)
1234: .toString();
1235:
1236: // Place various useful servlet-related objects in
1237: // the MessageContext object being delivered to the
1238: // plugin.
1239: msgContext.setProperty(
1240: MessageContext.TRANS_URL, url);
1241: msgContext.setProperty(
1242: HTTPConstants.PLUGIN_SERVICE_NAME,
1243: serviceName);
1244: msgContext.setProperty(
1245: HTTPConstants.PLUGIN_NAME, handlerName);
1246: msgContext.setProperty(
1247: HTTPConstants.PLUGIN_IS_DEVELOPMENT,
1248: new Boolean(isDevelopment()));
1249: msgContext.setProperty(
1250: HTTPConstants.PLUGIN_ENABLE_LIST,
1251: new Boolean(enableList));
1252: msgContext.setProperty(
1253: HTTPConstants.PLUGIN_ENGINE, engine);
1254: msgContext.setProperty(
1255: HTTPConstants.PLUGIN_WRITER, writer);
1256: msgContext.setProperty(
1257: HTTPConstants.PLUGIN_LOG, log);
1258: msgContext.setProperty(
1259: HTTPConstants.PLUGIN_EXCEPTION_LOG,
1260: exceptionLog);
1261:
1262: // Invoke the plugin.
1263:
1264: pluginMethod.invoke(plugin.newInstance(),
1265: new Object[] { msgContext });
1266:
1267: writer.close();
1268:
1269: return true;
1270: } catch (InvocationTargetException ie) {
1271: reportTroubleInGet(ie.getTargetException(),
1272: response, writer);
1273: // return true to prevent any further processing
1274: return true;
1275: } catch (Exception e) {
1276: reportTroubleInGet(e, response, writer);
1277: // return true to prevent any further processing
1278: return true;
1279: }
1280: }
1281: }
1282: }
1283:
1284: return false;
1285: }
1286:
1287: /**
1288: * getRequestPath a returns request path for web service padded with
1289: * request.getPathInfo for web services served from /services directory.
1290: * This is a required to support serving .jws web services from /services
1291: * URL. See AXIS-843 for more information.
1292: *
1293: * @param request HttpServletRequest
1294: * @return String
1295: */
1296: private static String getRequestPath(HttpServletRequest request) {
1297: return request.getServletPath()
1298: + ((request.getPathInfo() != null) ? request
1299: .getPathInfo() : "");
1300: }
1301: }
|