| AxisServlet.java |
/*
* The Apache Software License, Version 1.1
*
*
* Copyright (c) 2001-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Axis" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.jboss.axis.transport.http;
import org.jboss.axis.AxisEngine;
import org.jboss.axis.AxisFault;
import org.jboss.axis.ConfigurationException;
import org.jboss.axis.Constants;
import org.jboss.axis.Message;
import org.jboss.axis.MessageContext;
import org.jboss.axis.MessagePart;
import org.jboss.axis.description.OperationDesc;
import org.jboss.axis.description.ServiceDesc;
import org.jboss.axis.handlers.soap.SOAPService;
import org.jboss.axis.security.servlet.ServletSecurityProvider;
import org.jboss.axis.utils.JavaUtils;
import org.jboss.axis.utils.Messages;
import org.jboss.axis.utils.XMLUtils;
import org.jboss.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.soap.SOAPException;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.StringTokenizer;
/**
* @author Doug Davis (dug@us.ibm.com)
* @author Steve Loughran
*/
public class AxisServlet extends AxisServletBase
{
private static Logger log = Logger.getLogger(AxisServlet.class.getName());
/**
* this log is for timing
*/
private static Logger tlog = Logger.getLogger(Constants.TIME_LOG_CATEGORY);
public static final String INIT_PROPERTY_TRANSPORT_NAME = "transport.name";
public static final String INIT_PROPERTY_USE_SECURITY = "use-servlet-security";
public static final String INIT_PROPERTY_ENABLE_LIST = "axis.enableListQuery";
public static final String INIT_PROPERTY_JWS_CLASS_DIR = "axis.jws.servletClassDir";
// These have default values.
private String transportName;
private ServletSecurityProvider securityProvider = null;
/**
* Cached path to JWS output directory
*/
private String jwsClassDir = null;
protected String getJWSClassDir()
{
return jwsClassDir;
}
/**
* create a new servlet instance
*/
public AxisServlet()
{
}
/**
* Initialization method.
*/
public void init()
{
super.init();
ServletContext context = getServletConfig().getServletContext();
log.debug("In servlet init");
transportName = getOption(context,
INIT_PROPERTY_TRANSPORT_NAME,
HTTPTransport.DEFAULT_TRANSPORT_NAME);
if (JavaUtils.isTrueExplicitly(getOption(context, INIT_PROPERTY_USE_SECURITY, null)))
{
securityProvider = new ServletSecurityProvider();
}
jwsClassDir = getOption(context, INIT_PROPERTY_JWS_CLASS_DIR, null);
/**
* There are DEFINATE problems here if
* getHomeDir and/or getDefaultJWSClassDir return null
* (as they could with WebLogic).
* This needs to be reexamined in the future, but this
* should fix any NPE's in the mean time.
*/
if (jwsClassDir != null)
{
if (getHomeDir() != null)
{
jwsClassDir = getHomeDir() + jwsClassDir;
}
}
else
{
jwsClassDir = getDefaultJWSClassDir();
}
}
/**
* Process GET requests. This includes handoff of pseudo-SOAP requests
*
* @param request request in
* @param response request out
* @throws ServletException
* @throws IOException
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
log.debug("Enter: doGet()");
PrintWriter writer = response.getWriter();
try
{
AxisEngine engine = getEngine();
ServletContext servletContext =
getServletConfig().getServletContext();
String pathInfo = request.getPathInfo();
String realpath = servletContext.getRealPath(request.getServletPath());
if (realpath == null)
{
realpath = request.getServletPath();
}
boolean wsdlRequested = false;
boolean hasParameters = request.getParameterNames().hasMoreElements();
//JWS pages are special; they are the servlet path and there
//is no pathinfo...we map the pathinfo to the servlet path to keep
//it happy
boolean isJWSPage = request.getRequestURI().endsWith(".jws");
if (isJWSPage)
{
pathInfo = request.getServletPath();
}
// check first if we are doing WSDL or a list operation
String queryString = request.getQueryString();
if (queryString != null)
{
if (queryString.equalsIgnoreCase("wsdl"))
{
wsdlRequested = true;
}
}
boolean hasNoPath = (pathInfo == null || pathInfo.equals(""));
if (!wsdlRequested && hasNoPath)
{
// If the user requested the servlet (i.e. /axis/servlet/AxisServlet)
// with no service name, present the user with a list of deployed
// services to be helpful
// Don't do this if we are doing WSDL or list.
reportAvailableServices(response, writer, request);
}
else if (realpath != null)
{
// We have a pathname, so now we perform WSDL or list operations
// get message context w/ various properties set
MessageContext msgContext = createMessageContext(engine, request, response);
String url = request.getRequestURL().toString();
msgContext.setProperty(MessageContext.TRANS_URL, url);
if (wsdlRequested)
{
// Do WSDL generation
processWsdlRequest(msgContext, response, writer);
}
else if (hasParameters)
{
// If we have ?method=x¶m=y in the URL, make a stab
// at invoking the method with the parameters specified
// in the URL
processMethodRequest(msgContext, request, response, writer);
}
else
{
// See if we can locate the desired service. If we
// can't, return a 404 Not Found. Otherwise, just
// print the placeholder message.
String serviceName;
if (pathInfo.startsWith("/"))
{
serviceName = pathInfo.substring(1);
}
else
{
serviceName = pathInfo;
}
SOAPService s = engine.getService(serviceName);
if (s == null)
{
//no service: report it
if (isJWSPage)
{
reportCantGetJWSService(request, response, writer);
}
else
{
reportCantGetAxisService(request, response, writer);
}
}
else
{
//print a snippet of service info.
reportServiceInfo(response, writer, s, serviceName);
}
}
}
else
{
// We didn't have a real path in the request, so just
// print a message informing the user that they reached
// the servlet.
response.setContentType("text/html");
writer.println("<html><h1>Axis HTTP Servlet</h1>");
writer.println(Messages.getMessage("reachedServlet00"));
writer.println("<p>" +
Messages.getMessage("transportName00",
"<b>" + transportName + "</b>"));
writer.println("</html>");
}
}
catch (AxisFault fault)
{
reportTrouble(fault, response, writer);
}
catch (Exception e)
{
reportTrouble(e, response, writer);
}
finally
{
// Make sure the MessageContext is removed from the calling ThreadLocal
AxisEngine.setCurrentMessageContext(null);
writer.close();
log.debug("Exit: doGet()");
}
}
/**
* when we get an exception or an axis fault in a GET, we handle
* it almost identically: we go 'something went wrong', set the response
* code to 500 and then dump info. But we dump different info for an axis fault
* or subclass thereof.
*
* @param exception what went wrong
* @param response current response
* @param writer open writer to response
*/
protected void reportTrouble(Exception exception, HttpServletResponse response, PrintWriter writer)
{
response.setContentType("text/html");
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
setupHTMLResponseHeader(response, writer);
writer.println("<h2>" + Messages.getMessage("error00") + "</h2>");
writer.println("<p>" + Messages.getMessage("somethingWrong00") + "</p>");
if (exception instanceof AxisFault)
{
AxisFault fault = (AxisFault)exception;
processAxisFault(fault);
writeFault(writer, fault);
}
else
{
logException(exception);
writer.println("<pre>" + exception + "<br>");
//dev systems only give fault dumps
if (isDevelopment())
{
writer.println(JavaUtils.stackToString(exception));
}
writer.println("</pre>");
}
}
protected void setupHTMLResponseHeader(HttpServletResponse response, PrintWriter writer)
{
}
/**
* routine called whenever an axis fault is caught; where they
* are logged and any other business. The method may modify the fault
* in the process
*
* @param fault what went wrong.
*/
protected void processAxisFault(AxisFault fault)
{
//log the fault
Element runtimeException = fault.lookupFaultDetail(Constants.QNAME_FAULTDETAIL_RUNTIMEEXCEPTION);
if (runtimeException != null)
{
fault.removeFaultDetail(Constants.QNAME_FAULTDETAIL_RUNTIMEEXCEPTION);
}
//dev systems only give fault dumps
if (!isDevelopment())
{
//strip out the stack trace
fault.removeFaultDetail(Constants.QNAME_FAULTDETAIL_STACKTRACE);
}
log.info(fault.detail != null ? fault.detail : fault);
}
/**
* log any exception to our output log, at our chosen level
*
* @param e what went wrong
*/
protected void logException(Exception e)
{
log.info(Messages.getMessage("exception00"), e);
}
/**
* this method writes a fault out to an HTML stream. This includes
* escaping the strings to defend against cross-site scripting attacks
*
* @param writer
* @param axisFault
*/
private void writeFault(PrintWriter writer, AxisFault axisFault)
{
String localizedMessage = XMLUtils.xmlEncodeString(axisFault.getLocalizedMessage());
writer.println("<pre>Fault - " + localizedMessage + "<br>");
writer.println(axisFault.dumpToString());
writer.println("</pre>");
}
/**
* scan through the request for parameters, invoking the endpoint
* if we get a method param. If there was no method param then the
* response is set to a 400 Bad Request and some error text
*
* @param msgContext current message
* @param request incoming requests
* @param response response to generate
* @param writer output stream
* @throws AxisFault if anything goes wrong during method execution
*/
protected void processMethodRequest(MessageContext msgContext,
HttpServletRequest request,
HttpServletResponse response,
PrintWriter writer) throws AxisFault
{
Enumeration en = request.getParameterNames();
String method = null;
String args = "";
while (en.hasMoreElements())
{
String param = (String)en.nextElement();
if (param.equalsIgnoreCase("method"))
{
method = request.getParameter(param);
}
else
{
args += "<" + param + ">" +
request.getParameter(param) +
"</" + param + ">";
}
}
if (method == null)
{
response.setContentType("text/html");
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
writer.println("<h2>" +
Messages.getMessage("error00") +
": " +
Messages.getMessage("invokeGet00") +
"</h2>");
writer.println("<p>" +
Messages.getMessage("noMethod01") +
"</p>");
}
else
{
invokeEndpointFromGet(msgContext, response, writer, method, args);
}
}
/**
* handle a ?wsdl request
*
* @param msgContext message context so far
* @param response response to write to
* @param writer output stream
* @throws AxisFault when anything other than a Server.NoService fault is reported
* during WSDL generation
*/
protected void processWsdlRequest(MessageContext msgContext,
HttpServletResponse response,
PrintWriter writer) throws AxisFault
{
AxisEngine engine = getEngine();
try
{
engine.generateWSDL(msgContext);
Document doc = (Document)msgContext.getProperty("WSDL");
if (doc != null)
{
response.setContentType("text/xml");
XMLUtils.DocumentToWriter(doc, writer);
}
else
{
if (log.isDebugEnabled())
{
log.debug("processWsdlRequest: failed to create WSDL");
}
reportNoWSDL(response, writer, "noWSDL02", null);
}
}
catch (AxisFault axisFault)
{
//the no-service fault is mapped to a no-wsdl error
if (axisFault.getFaultCode().equals(Constants.QNAME_NO_SERVICE_FAULT_CODE))
{
//which we log
processAxisFault(axisFault);
//then report under a 404 error
response.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
reportNoWSDL(response, writer, "noWSDL01", axisFault);
}
else
{
//all other faults get thrown
throw axisFault;
}
}
}
/**
* invoke an endpoint from a get request by building an XML request and
* handing it down. If anything goes wrong, we generate an XML formatted
* axis fault
*
* @param msgContext current message
* @param response to return data
* @param writer output stream
* @param method method to invoke (may be null)
* @param args argument list in XML form
* @throws AxisFault iff something goes wrong when turning the response message
* into a SOAP string.
*/
protected void invokeEndpointFromGet(MessageContext msgContext,
HttpServletResponse response,
PrintWriter writer,
String method,
String args) throws AxisFault
{
String body =
"<" + method + ">" + args + "</" + method + ">";
String msgtxt =
"<SOAP-ENV:Envelope" +
" xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
"<SOAP-ENV:Body>" + body + "</SOAP-ENV:Body>" +
"</SOAP-ENV:Envelope>";
Message responseMsg = null;
try
{
ByteArrayInputStream istream =
new ByteArrayInputStream(msgtxt.getBytes(XMLUtils.httpAuthCharEncoding));
AxisEngine engine = getEngine();
Message msg = new Message(istream, false);
msgContext.setRequestMessage(msg);
engine.invoke(msgContext);
responseMsg = msgContext.getResponseMessage();
//turn off caching for GET requests
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");
if (responseMsg == null)
{
//tell everyone that something is wrong
throw new Exception(Messages.getMessage("noResponse01"));
}
}
catch (AxisFault fault)
{
processAxisFault(fault);
configureResponseFromAxisFault(response, fault);
if (responseMsg == null)
{
responseMsg = new Message(fault);
msgContext.setResponseMessage(responseMsg);
}
}
catch (Exception e)
{
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
responseMsg = convertExceptionToAxisFault(e, responseMsg);
}
//this call could throw an AxisFault. We delegate it up, because
//if we cant write the message there is not a lot we can do in pure SOAP terms.
response.setContentType("text/xml");
writer.println(responseMsg.getSOAPPartAsString());
}
/**
* print a snippet of service info.
*
* @param service service
* @param writer output channel
* @param serviceName where to put stuff
*/
protected void reportServiceInfo(HttpServletResponse response, PrintWriter writer, SOAPService service, String serviceName)
{
response.setContentType("text/html");
writer.println("<h1>"
+ service.getName()
+ "</h1>");
writer.println("<p>" +
Messages.getMessage("axisService00") +
"</p>");
writer.println("<i>" +
Messages.getMessage("perhaps00") +
"</i>");
}
/**
* report that we have no WSDL
*
* @param res
* @param writer
* @param moreDetailCode optional name of a message to provide more detail
* @param axisFault optional fault string, for extra info at debug time only
*/
protected void reportNoWSDL(HttpServletResponse res, PrintWriter writer,
String moreDetailCode, AxisFault axisFault)
{
res.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
res.setContentType("text/html");
writer.println("<h2>" +
Messages.getMessage("error00") +
"</h2>");
writer.println("<p>" +
Messages.getMessage("noWSDL00") +
"</p>");
if (moreDetailCode != null)
{
writer.println("<p>"
+ Messages.getMessage(moreDetailCode)
+ "</p>");
}
if (axisFault != null && isDevelopment())
{
//dev systems only give fault dumps
writeFault(writer, axisFault);
}
}
/**
* This method lists the available services; it is called when there is
* nothing to execute on a GET
*
* @param response
* @param writer
* @param request
* @throws ConfigurationException
* @throws AxisFault
*/
protected void reportAvailableServices(HttpServletResponse response,
PrintWriter writer,
HttpServletRequest request)
throws ConfigurationException, AxisFault
{
AxisEngine engine = getEngine();
response.setContentType("text/html");
writer.println("<h2>And now... Some Services</h2>");
Iterator i;
try
{
i = engine.getConfig().getDeployedServices();
}
catch (ConfigurationException configException)
{
//turn any internal configuration exceptions back into axis faults
//if that is what they are
if (configException.getContainedException() instanceof AxisFault)
{
throw (AxisFault)configException.getContainedException();
}
else
{
throw configException;
}
}
String baseURL = getWebappBase(request) + "/services/";
writer.println("<ul>");
while (i.hasNext())
{
ServiceDesc sd = (ServiceDesc)i.next();
StringBuffer sb = new StringBuffer();
sb.append("<li>");
String name = sd.getName();
sb.append(name);
sb.append(" <a href=\"");
sb.append(baseURL);
sb.append(name);
sb.append("?wsdl\"><i>(wsdl)</i></a></li>");
writer.println(sb.toString());
ArrayList operations = sd.getOperations();
if (!operations.isEmpty())
{
writer.println("<ul>");
for (Iterator it = operations.iterator(); it.hasNext();)
{
OperationDesc desc = (OperationDesc)it.next();
writer.println("<li>" + desc.getName());
}
writer.println("</ul>");
}
}
writer.println("</ul>");
}
/**
* generate the error response to indicate that there is apparently no endpoint there
*
* @param request the request that didnt have an edpoint
* @param response response we are generating
* @param writer open writer for the request
*/
protected void reportCantGetAxisService(HttpServletRequest request, HttpServletResponse response, PrintWriter writer)
{
// no such service....
response.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
response.setContentType("text/html");
writer.println("<h2>" +
Messages.getMessage("error00") + "</h2>");
writer.println("<p>" +
Messages.getMessage("noService06") +
"</p>");
}
/**
* probe for a JWS page and report 'no service' if one is not found there
*
* @param request the request that didnt have an edpoint
* @param response response we are generating
* @param writer open writer for the request
*/
protected void reportCantGetJWSService(HttpServletRequest request, HttpServletResponse response, PrintWriter writer)
{
//first look to see if there is a service
String realpath =
getServletConfig().getServletContext()
.getRealPath(request.getServletPath());
boolean foundJWSFile = (new File(realpath).exists()) &&
(realpath.endsWith(Constants.JWS_DEFAULT_FILE_EXTENSION));
response.setContentType("text/html");
if (foundJWSFile)
{
response.setStatus(HttpURLConnection.HTTP_OK);
writer.println(Messages.getMessage("foundJWS00") + "<p>");
String url = request.getRequestURI();
String urltext = Messages.getMessage("foundJWS01");
writer.println("<a href='" + url + "?wsdl'>" + urltext + "</a>");
}
else
{
response.setStatus(HttpURLConnection.HTTP_NOT_FOUND);
writer.println(Messages.getMessage("noService06"));
}
}
/**
* Process a POST to the servlet by handing it off to the Axis Engine.
* Here is where SOAP messages are received
* <p/>
* 1. get the Axis server engine
* 2. create the MessageContext and populate it with the req/res objects
* 3. set security provider
* 4. create request SOAPMessage
* 5. set the SOAPAction in the message context
* 6. invoke the engine
* 7. get response SOAPMessage
* 8. get the response content type
* 9. send the response
*
* @param req posted request
* @param res respose
* @throws ServletException trouble
* @throws IOException different trouble
*/
public void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
long t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0;
String soapAction = null;
MessageContext msgContext = null;
log.debug("Enter: doPost()");
if (tlog.isDebugEnabled())
{
t0 = System.currentTimeMillis();
}
Message responseMsg = null;
String contentType = null;
try
{
AxisEngine engine = getEngine();
if (engine == null)
{
// !!! should return a SOAP fault...
ServletException se =
new ServletException(Messages.getMessage("noEngine00"));
log.debug("No Engine!", se);
throw se;
}
res.setBufferSize(1024 * 8); // provide performance boost.
/** get message context w/ various properties set
*/
msgContext = createMessageContext(engine, req, res);
String serviceName = getServiceName(req);
log.debug("serviceName: " + serviceName);
// Get the SOAP servie
SOAPService service = (serviceName != null ? engine.getService(serviceName) : null);
if (service != null)
msgContext.setTargetService(serviceName);
// ? OK to move this to 'getMessageContext',
// ? where it would also be picked up for 'doGet()' ?
if (securityProvider != null)
{
log.debug("securityProvider:" + securityProvider);
msgContext.setProperty(MessageContext.SECURITY_PROVIDER, securityProvider);
}
InputStream inputStream = req.getInputStream();
/* Get request message
*/
Message requestMsg =
new Message(inputStream, false,
req.getHeader(HTTPConstants.HEADER_CONTENT_TYPE),
req.getHeader(HTTPConstants.HEADER_CONTENT_LOCATION));
if (log.isDebugEnabled())
{
log.debug("XML request received");
log.debug("----------------------------------------------");
MessagePart msgPart = (MessagePart)requestMsg.getSOAPPart();
String xmlMessage = new String(msgPart.getAsBytes());
log.debug("----------------------------------------------");
log.debug(xmlMessage);
log.debug("----------------------------------------------");
}
/* Set the request(incoming) message field in the context */
/**********************************************************/
msgContext.setRequestMessage(requestMsg);
String url = req.getRequestURL().toString();
msgContext.setProperty(MessageContext.TRANS_URL, url);
try
{
/**
* Save the SOAPAction header in the MessageContext bag.
* This will be used to tell the Axis Engine which service
* is being invoked. This will save us the trouble of
* having to parse the Request message - although we will
* need to double-check later on that the SOAPAction header
* does in fact match the URI in the body.
*/
// (is this last stmt true??? (I don't think so - Glen))
/********************************************************/
soapAction = getSoapAction(req);
if (soapAction != null)
{
msgContext.setUseSOAPAction(true);
msgContext.setSOAPActionURI(soapAction);
}
// Create a Session wrapper for the HTTP session.
// These can/should be pooled at some point.
// (Sam is Watching! :-)
msgContext.setSession(new AxisHttpSession(req));
if (tlog.isDebugEnabled())
{
t1 = System.currentTimeMillis();
}
/* Invoke the Axis engine... */
/*****************************/
log.debug("Invoking Axis Engine.");
engine.invoke(msgContext);
log.debug("Return from Axis Engine.");
if (tlog.isDebugEnabled())
{
t2 = System.currentTimeMillis();
}
OperationDesc opDesc = msgContext.getOperation();
boolean isOneWay = opDesc != null && opDesc.isOneWay();
responseMsg = msgContext.getResponseMessage();
if (responseMsg == null && isOneWay == false)
{
//tell everyone that something is wrong
throw new Exception(Messages.getMessage("noResponse01"));
}
}
catch (AxisFault fault)
{
//log and sanitize
processAxisFault(fault);
configureResponseFromAxisFault(res, fault);
responseMsg = msgContext.getResponseMessage();
if (responseMsg == null)
{
responseMsg = new Message(fault);
msgContext.setResponseMessage(responseMsg);
}
}
catch (Exception e)
{
//other exceptions are internal trouble
responseMsg = msgContext.getResponseMessage();
res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
responseMsg = convertExceptionToAxisFault(e, responseMsg);
}
}
catch (AxisFault fault)
{
processAxisFault(fault);
configureResponseFromAxisFault(res, fault);
responseMsg = msgContext.getResponseMessage();
if (responseMsg == null)
{
responseMsg = new Message(fault);
msgContext.setResponseMessage(responseMsg);
}
}
finally
{
// Make sure the MessageContext is removed from the calling ThreadLocal
AxisEngine.setCurrentMessageContext(null);
}
//determine content type from message response
if (tlog.isDebugEnabled())
{
t3 = System.currentTimeMillis();
}
/* Send response back along the wire... */
/***********************************/
if (responseMsg != null)
{
contentType = responseMsg.getContentType(msgContext.getSOAPConstants());
sendResponse(contentType,
res, responseMsg);
}
log.debug("Response sent.");
log.debug("Exit: doPost()");
if (tlog.isDebugEnabled())
{
t4 = System.currentTimeMillis();
tlog.debug("axisServlet.doPost: " + soapAction +
" pre=" + (t1 - t0) +
" invoke=" + (t2 - t1) +
" post=" + (t3 - t2) +
" send=" + (t4 - t3) +
" " + msgContext.getTargetService() + "." +
((msgContext.getOperation() == null) ?
"" : msgContext.getOperation().getName()));
}
}
/**
* Assumes that the service name is the first token in the path info
*/
protected String getServiceName(HttpServletRequest request)
{
String path = request.getPathInfo();
if (path != null)
{
StringTokenizer st = new StringTokenizer(path, "/");
if (st.hasMoreTokens())
{
return st.nextToken();
}
}
return null;
}
/**
* Configure the servlet response status code and maybe other headers
* from the fault info.
*
* @param response response to configure
* @param fault what went wrong
*/
private void configureResponseFromAxisFault(HttpServletResponse response,
AxisFault fault)
{
// then get the status code
// It's been suggested that a lack of SOAPAction
// should produce some other error code (in the 400s)...
int status = getHttpServletResponseStatus(fault);
if (status == HttpServletResponse.SC_UNAUTHORIZED)
{
// unauth access results in authentication request
// TODO: less generic realm choice?
response.setHeader("WWW-Authenticate", "Basic realm=\"AXIS\"");
}
response.setStatus(status);
}
/**
* turn any Exception into an AxisFault, log it, set the response
* status code according to what the specifications say and
* return a response message for posting. This will be the response
* message passed in if non-null; one generated from the fault otherwise.
*
* @param exception what went wrong
* @param responseMsg what response we have (if any)
* @return a response message to send to the user
*/
private Message convertExceptionToAxisFault(Exception exception,
Message responseMsg)
{
logException(exception);
if (responseMsg == null)
{
AxisFault fault = AxisFault.makeFault(exception);
processAxisFault(fault);
responseMsg = new Message(fault);
}
return responseMsg;
}
/**
* Extract information from AxisFault and map it to a HTTP Status code.
*
* @param af Axis Fault
* @return HTTP Status code.
*/
protected int getHttpServletResponseStatus(AxisFault af)
{
// TODO: Should really be doing this with explicit AxisFault
// subclasses... --Glen
return af.getFaultCode().getLocalPart().startsWith("Server.Unauth")
? HttpServletResponse.SC_UNAUTHORIZED
: HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
// This will raise a 401 for both
// "Unauthenticated" & "Unauthorized"...
}
/**
* write a message to the response, set appropriate headers for content
* type..etc.
*
* @param res response
* @param responseMsg message to write
* @throws AxisFault
* @throws IOException if the response stream can not be written to
*/
private void sendResponse(String contentType, HttpServletResponse res, Message responseMsg)
throws AxisFault, IOException
{
if (responseMsg == null)
{
res.setStatus(HttpServletResponse.SC_NO_CONTENT);
log.debug("NO AXIS MESSAGE TO RETURN!");
//String resp = Messages.getMessage("noData00");
//res.setContentLength((int) resp.getBytes().length);
//res.getWriter().print(resp);
}
else
{
log.debug("Returned Content-Type:" + contentType);
// log.debug("Returned Content-Length:" +
// responseMsg.getContentLength());
try
{
res.setContentType(contentType);
/* My understand of Content-Length
* HTTP 1.0
* -Required for requests, but optional for responses.
* HTTP 1.1
* - Either Content-Length or HTTP Chunking is required.
* Most servlet engines will do chunking if content-length is not specified.
*
*
*/
//if(clientVersion == HTTPConstants.HEADER_PROTOCOL_V10) //do chunking if necessary.
// res.setContentLength(responseMsg.getContentLength());
responseMsg.writeTo(res.getOutputStream());
if (log.isDebugEnabled())
{
log.debug("XML response sent");
log.debug("----------------------------------------------");
MessagePart msgPart = (MessagePart)responseMsg.getSOAPPart();
String xmlMessage = msgPart.getAsString();
log.debug("----------------------------------------------");
log.debug(xmlMessage);
log.debug("----------------------------------------------");
}
}
catch (SOAPException e)
{
logException(e);
}
}
if (!res.isCommitted())
{
res.flushBuffer(); // Force it right now.
}
}
/**
* Place the Request message in the MessagContext object - notice
* that we just leave it as a 'ServletRequest' object and let the
* Message processing routine convert it - we don't do it since we
* don't know how it's going to be used - perhaps it might not
* even need to be parsed.
*
* @return a message context
*/
protected MessageContext createMessageContext(AxisEngine engine,
HttpServletRequest req,
HttpServletResponse res)
{
MessageContext msgContext = new MessageContext(engine);
AxisEngine.setCurrentMessageContext(msgContext);
if (log.isDebugEnabled())
{
log.debug("MessageContext:" + msgContext);
log.debug("HEADER_CONTENT_TYPE:" +
req.getHeader(HTTPConstants.HEADER_CONTENT_TYPE));
log.debug("HEADER_CONTENT_LOCATION:" +
req.getHeader(HTTPConstants.HEADER_CONTENT_LOCATION));
log.debug("Constants.MC_HOME_DIR:" + String.valueOf(getHomeDir()));
log.debug("Constants.MC_RELATIVE_PATH:" + req.getServletPath());
log.debug("HTTPConstants.MC_HTTP_SERVLETLOCATION:" + String.valueOf(getWebInfPath()));
log.debug("HTTPConstants.MC_HTTP_SERVLETPATHINFO:" +
req.getPathInfo());
log.debug("HTTPConstants.HEADER_AUTHORIZATION:" +
req.getHeader(HTTPConstants.HEADER_AUTHORIZATION));
log.debug("Constants.MC_REMOTE_ADDR:" + req.getRemoteAddr());
log.debug("configPath:" + String.valueOf(getWebInfPath()));
}
/* Set the Transport */
/*********************/
msgContext.setTransportName(transportName);
/* Save some HTTP specific info in the bag in case someone needs it */
/********************************************************************/
msgContext.setProperty(Constants.MC_JWS_CLASSDIR, jwsClassDir);
msgContext.setProperty(Constants.MC_HOME_DIR, getHomeDir());
msgContext.setProperty(Constants.MC_RELATIVE_PATH,
req.getServletPath());
msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLET, this);
msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST, req);
msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETRESPONSE, res);
msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETLOCATION,
getWebInfPath());
msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETPATHINFO,
req.getPathInfo());
msgContext.setProperty(HTTPConstants.HEADER_AUTHORIZATION,
req.getHeader(HTTPConstants.HEADER_AUTHORIZATION));
msgContext.setProperty(Constants.MC_REMOTE_ADDR, req.getRemoteAddr());
// Set up a javax.xml.rpc.server.ServletEndpointContext
ServletEndpointContextImpl sec = new ServletEndpointContextImpl();
msgContext.setProperty(Constants.MC_SERVLET_ENDPOINT_CONTEXT, sec);
/* Save the real path */
/**********************/
String realpath =
getServletConfig().getServletContext()
.getRealPath(req.getServletPath());
if (realpath != null)
{
msgContext.setProperty(Constants.MC_REALPATH, realpath);
}
msgContext.setProperty(Constants.MC_CONFIGPATH, getWebInfPath());
return msgContext;
}
/**
* Extract the SOAPAction header.
* if SOAPAction is null then we'll we be forced to scan the body for it.
* if SOAPAction is "" then use the URL
*
* @param req incoming request
* @return the action
* @throws AxisFault
*/
private String getSoapAction(HttpServletRequest req)
throws AxisFault
{
String soapAction = req.getHeader(HTTPConstants.HEADER_SOAP_ACTION);
log.debug("HEADER_SOAP_ACTION:" + soapAction);
/**
* Technically, if we don't find this header, we should probably fault.
* It's required in the SOAP HTTP binding.
*/
if (soapAction == null)
{
AxisFault af = new AxisFault("Client.NoSOAPAction",
Messages.getMessage("noHeader00",
"SOAPAction"),
null, null);
log.error(Messages.getMessage("genFault00"), af);
throw af;
}
if (soapAction.length() == 0)
soapAction = req.getContextPath(); // Is this right?
return soapAction;
}
/**
* Provided to allow overload of default JWSClassDir
* by derived class.
*
* @return directory for JWS files
*/
protected String getDefaultJWSClassDir()
{
return (getWebInfPath() == null)
? null // ??? what is a good FINAL default for WebLogic?
: getWebInfPath() + File.separator + "jwsClasses";
}
/**
* Return the HTTP protocol level 1.1 or 1.0
* by derived class.
*
* @return one of the HTTPConstants values
*/
protected String getProtocolVersion(HttpServletRequest req)
{
String ret = HTTPConstants.HEADER_PROTOCOL_V10;
String prot = req.getProtocol();
if (prot != null)
{
int sindex = prot.indexOf('/');
if (-1 != sindex)
{
String ver = prot.substring(sindex + 1);
if (HTTPConstants.HEADER_PROTOCOL_V11.equals(ver.trim()))
{
ret = HTTPConstants.HEADER_PROTOCOL_V11;
}
}
}
return ret;
}
}
| AxisServlet.java |