org.apache.axis.transport.http.AxisServlet Maven / Gradle / Ivy
/*
* Copyright 2001-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.axis.transport.http;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpUtils;
import javax.xml.soap.MimeHeader;
import javax.xml.soap.MimeHeaders;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import org.apache.axis.AxisEngine;
import org.apache.axis.AxisFault;
import org.apache.axis.ConfigurationException;
import org.apache.axis.Constants;
import org.apache.axis.Handler;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.SimpleTargetedChain;
import org.apache.axis.client.Service;
import org.apache.axis.management.ServiceAdmin;
import org.apache.axis.components.logger.LogFactory;
import org.apache.axis.description.OperationDesc;
import org.apache.axis.description.ServiceDesc;
import org.apache.axis.handlers.soap.SOAPService;
import org.apache.axis.security.servlet.ServletSecurityProvider;
import org.apache.axis.utils.JavaUtils;
import org.apache.axis.utils.Messages;
import org.apache.axis.utils.XMLUtils;
import org.apache.commons.logging.Log;
import org.w3c.dom.Element;
/**
*
* @author Doug Davis ([email protected])
* @author Steve Loughran
* xdoclet tags are not active yet; keep web.xml in sync.
* To change the location of the services, change url-pattern in web.xml and
* set parameter axis.servicesPath in server-config.wsdd. For more information see
* Axis Reference Guide.
*
* @web.servlet name="AxisServlet" display-name="Apache-Axis Servlet"
* @web.servlet-mapping url-pattern="/servlet/AxisServlet"
* @web.servlet-mapping url-pattern="*.jws"
* @web.servlet-mapping url-pattern="/services/*"
*/
public class AxisServlet extends AxisServletBase {
protected static Log log =
LogFactory.getLog(AxisServlet.class.getName());
/**
* this log is for timing
*/
private static Log tlog =
LogFactory.getLog(Constants.TIME_LOG_CATEGORY);
/**
* a separate log for exceptions lets users route them
* differently from general low level debug info
*/
private static Log exceptionLog =
LogFactory.getLog(Constants.EXCEPTION_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";
// This will turn off the list of available services
public static final String INIT_PROPERTY_DISABLE_SERVICES_LIST =
"axis.disableServiceList";
// Location of the services as defined by the servlet-mapping in web.xml
public static final String INIT_PROPERTY_SERVICES_PATH =
"axis.servicesPath";
// These have default values.
private String transportName;
private Handler transport;
private ServletSecurityProvider securityProvider = null;
private String servicesPath;
/**
* cache of logging debug option; only evaluated at init time.
* So no dynamic switching of logging options with this servlet.
*/
private static boolean isDebug = false;
/**
* Should we enable the "?list" functionality on GETs? (off by
* default because deployment information is a potential security
* hole)
*/
private boolean enableList = false;
/**
* Should we turn off the list of services when we receive a GET
* at the servlet root?
*/
private boolean disableServicesList = false;
/**
* 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() throws javax.servlet.ServletException {
super.init();
ServletContext context = getServletConfig().getServletContext();
isDebug = log.isDebugEnabled();
if (isDebug) {
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();
}
enableList =
JavaUtils.isTrueExplicitly(getOption(context,
INIT_PROPERTY_ENABLE_LIST, null));
jwsClassDir = getOption(context, INIT_PROPERTY_JWS_CLASS_DIR, null);
// Should we list services?
disableServicesList = JavaUtils.isTrue(getOption(context,
INIT_PROPERTY_DISABLE_SERVICES_LIST, "false"));
servicesPath = getOption(context, INIT_PROPERTY_SERVICES_PATH,
"/services/");
/**
* 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();
}
initQueryStringHandlers();
// Setup the service admin
try {
ServiceAdmin.setEngine(this.getEngine(), context.getServerInfo());
} catch (AxisFault af) {
exceptionLog.info("Exception setting AxisEngine on ServiceAdmin " +
af);
}
}
/**
* 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 {
if (isDebug) {
log.debug("Enter: doGet()");
}
PrintWriter writer = new FilterPrintWriter(response);
try {
AxisEngine engine = getEngine();
ServletContext servletContext =
getServletConfig().getServletContext();
String pathInfo = request.getPathInfo();
String realpath = servletContext.getRealPath(request.getServletPath());
if (realpath == null) {
realpath = request.getServletPath();
}
//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();
}
// Try to execute a query string plugin and return upon success.
if (processQuery(request, response, writer) == true) {
return;
}
boolean hasNoPath = (pathInfo == null || pathInfo.equals(""));
if (!disableServicesList) {
if(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 has been turned off
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);
// NOTE: HttpUtils.getRequestURL has been deprecated.
// This line SHOULD be:
// String url = req.getRequestURL().toString()
// HOWEVER!!!! DON'T REPLACE IT! There's a bug in
// req.getRequestURL that is not in HttpUtils.getRequestURL
// req.getRequestURL returns "localhost" in the remote
// scenario rather than the actual host name.
//
// But more importantly, getRequestURL() is a servlet 2.3
// API and to support servlet 2.2 (aka WebSphere 4)
// we need to leave this in for a while longer. tomj 10/14/2004
//
String url = HttpUtils.getRequestURL(request).toString();
msgContext.setProperty(MessageContext.TRANS_URL, url);
// 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; charset=utf-8");
writer.println("Axis HTTP Servlet
");
writer.println(Messages.getMessage("reachedServlet00"));
writer.println("" +
Messages.getMessage("transportName00",
"" + transportName + ""));
writer.println("");
}
} catch (AxisFault fault) {
reportTroubleInGet(fault, response, writer);
} catch (Exception e) {
reportTroubleInGet(e, response, writer);
} finally {
writer.close();
if (isDebug) {
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
*/
private void reportTroubleInGet(Throwable exception,
HttpServletResponse response,
PrintWriter writer) {
response.setContentType("text/html; charset=utf-8");
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
writer.println("
" +
Messages.getMessage("error00") +
"
");
writer.println("" +
Messages.getMessage("somethingWrong00") +
"
");
if (exception instanceof AxisFault) {
AxisFault fault = (AxisFault) exception;
processAxisFault(fault);
writeFault(writer, fault);
} else {
logException(exception);
writer.println("Exception - " + exception + "
");
//dev systems only give fault dumps
if (isDevelopment()) {
writer.println(JavaUtils.stackToString(exception));
}
writer.println("
");
}
}
/**
* 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) {
exceptionLog.info(Messages.getMessage("axisFault00"), fault);
//strip runtime details
fault.removeFaultDetail(Constants.
QNAME_FAULTDETAIL_RUNTIMEEXCEPTION);
} else if (exceptionLog.isDebugEnabled()) {
exceptionLog.debug(Messages.getMessage("axisFault00"), fault);
}
//dev systems only give fault dumps
if (!isDevelopment()) {
//strip out the stack trace
fault.removeFaultDetail(Constants.QNAME_FAULTDETAIL_STACKTRACE);
}
}
/**
* log any exception to our output log, at our chosen level
* @param e what went wrong
*/
protected void logException(Throwable e) {
exceptionLog.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("Fault - " + localizedMessage + "
");
writer.println(axisFault.dumpToString());
writer.println("
");
}
/**
* 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; charset=utf-8");
writer.println(""
+ service.getName()
+ "
");
writer.println(
"" +
Messages.getMessage("axisService00") +
"
");
writer.println(
"" +
Messages.getMessage("perhaps00") +
"");
}
/**
* report that we have no WSDL
*
* This method was moved to the querystring handler QSWSDLHandler. The
* method reportNoWSDL in AxisServlet is never called. Perhaps the method
* is overwritten in subclasses of AxisServlet so the method wasn't
* removed. See the discussion in
*
* http://nagoya.apache.org/bugzilla/show_bug.cgi?id=23845
*
* @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) {
}
/**
* 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; charset=utf-8");
writer.println("And now... Some Services
");
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;
}
}
// baseURL may change if tag is used for
// custom deployment at a different location
String defaultBaseURL = getWebappBase(request) + servicesPath;
writer.println("");
while (i.hasNext()) {
ServiceDesc sd = (ServiceDesc) i.next();
StringBuffer sb = new StringBuffer();
sb.append("- ");
String name = sd.getName();
sb.append(name);
sb.append(" (wsdl)
");
writer.println(sb.toString());
ArrayList operations = sd.getOperations();
if (!operations.isEmpty()) {
writer.println("");
for (Iterator it = operations.iterator(); it.hasNext(); ) {
OperationDesc desc = (OperationDesc) it.next();
writer.println("- " + desc.getName());
}
writer.println("
");
}
}
writer.println("
");
}
/**
* 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; charset=utf-8");
writer.println("" +
Messages.getMessage("error00") + "
");
writer.println("" +
Messages.getMessage("noService06") +
"
");
}
/**
* 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
// requestPath is a work around to support serving .jws web services
// from services URL - see AXIS-843 for more information
String requestPath = request.getServletPath() + ((request.getPathInfo() != null) ?
request.getPathInfo() : "");
String realpath = getServletConfig().getServletContext()
.getRealPath(requestPath);
log.debug("JWS real path: " + realpath);
boolean foundJWSFile = (new File(realpath).exists()) &&
(realpath.endsWith(Constants.
JWS_DEFAULT_FILE_EXTENSION));
response.setContentType("text/html; charset=utf-8");
if (foundJWSFile) {
response.setStatus(HttpURLConnection.HTTP_OK);
writer.println(Messages.getMessage("foundJWS00") + "");
String url = request.getRequestURI();
String urltext = Messages.getMessage("foundJWS01");
writer.println("" + urltext + "");
} 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
* @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;
if (isDebug) {
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);
// ? OK to move this to 'getMessageContext',
// ? where it would also be picked up for 'doGet()' ?
if (securityProvider != null) {
if (isDebug) {
log.debug("securityProvider:" + securityProvider);
}
msgContext.setProperty(MessageContext.SECURITY_PROVIDER,
securityProvider);
}
/* Get request message
*/
Message requestMsg =
new Message(req.getInputStream(),
false,
req.getHeader(HTTPConstants.HEADER_CONTENT_TYPE),
req.getHeader(HTTPConstants.
HEADER_CONTENT_LOCATION));
// Transfer HTTP headers to MIME headers for request message.
MimeHeaders requestMimeHeaders = requestMsg.getMimeHeaders();
for (Enumeration e = req.getHeaderNames(); e.hasMoreElements(); ) {
String headerName = (String) e.nextElement();
for (Enumeration f = req.getHeaders(headerName);
f.hasMoreElements(); ) {
String headerValue = (String) f.nextElement();
requestMimeHeaders.addHeader(headerName, headerValue);
}
}
if (isDebug) {
log.debug("Request Message:" + requestMsg);
/* Set the request(incoming) message field in the context */
/**********************************************************/
}
msgContext.setRequestMessage(requestMsg);
String url = HttpUtils.getRequestURL(req).toString();
msgContext.setProperty(MessageContext.TRANS_URL, url);
// put character encoding of request to message context
// in order to reuse it during the whole process.
String requestEncoding;
try {
requestEncoding = (String) requestMsg.getProperty(SOAPMessage.
CHARACTER_SET_ENCODING);
if (requestEncoding != null) {
msgContext.setProperty(SOAPMessage.CHARACTER_SET_ENCODING,
requestEncoding);
}
} catch (SOAPException e1) {
}
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... */
/*****************************/
if (isDebug) {
log.debug("Invoking Axis Engine.");
//here we run the message by the engine
}
engine.invoke(msgContext);
if (isDebug) {
log.debug("Return from Axis Engine.");
}
if (tlog.isDebugEnabled()) {
t2 = System.currentTimeMillis();
}
responseMsg = msgContext.getResponseMessage();
// We used to throw exceptions on null response messages.
// They are actually OK in certain situations (asynchronous
// services), so fall through here and return an ACCEPTED
// status code below. Might want to install a configurable
// error check for this later.
} catch (AxisFault fault) {
//log and sanitize
processAxisFault(fault);
configureResponseFromAxisFault(res, fault);
responseMsg = msgContext.getResponseMessage();
if (responseMsg == null) {
responseMsg = new Message(fault);
((org.apache.axis.SOAPPart) responseMsg.getSOAPPart()).
getMessage().setMessageContext(msgContext);
}
} catch (Exception e) {
//other exceptions are internal trouble
responseMsg = msgContext.getResponseMessage();
res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
responseMsg = convertExceptionToAxisFault(e, responseMsg);
((org.apache.axis.SOAPPart) responseMsg.getSOAPPart()).
getMessage().setMessageContext(msgContext);
} catch (Throwable t) {
logException(t);
//other exceptions are internal trouble
responseMsg = msgContext.getResponseMessage();
res.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
responseMsg = new Message(new AxisFault(t.toString(),t));
((org.apache.axis.SOAPPart) responseMsg.getSOAPPart()).
getMessage().setMessageContext(msgContext);
}
} catch (AxisFault fault) {
processAxisFault(fault);
configureResponseFromAxisFault(res, fault);
responseMsg = msgContext.getResponseMessage();
if (responseMsg == null) {
responseMsg = new Message(fault);
((org.apache.axis.SOAPPart) responseMsg.getSOAPPart()).
getMessage().setMessageContext(msgContext);
}
}
if (tlog.isDebugEnabled()) {
t3 = System.currentTimeMillis();
}
/* Send response back along the wire... */
/***********************************/
if (responseMsg != null) {
// Transfer MIME headers to HTTP headers for response message.
MimeHeaders responseMimeHeaders = responseMsg.getMimeHeaders();
for (Iterator i = responseMimeHeaders.getAllHeaders(); i.hasNext(); ) {
MimeHeader responseMimeHeader = (MimeHeader) i.next();
res.addHeader(responseMimeHeader.getName(),
responseMimeHeader.getValue());
}
// synchronize the character encoding of request and response
String responseEncoding = (String) msgContext.getProperty(
SOAPMessage.CHARACTER_SET_ENCODING);
if (responseEncoding != null) {
try {
responseMsg.setProperty(SOAPMessage.CHARACTER_SET_ENCODING,
responseEncoding);
} catch (SOAPException e) {
}
}
//determine content type from message response
contentType = responseMsg.getContentType(msgContext.
getSOAPConstants());
sendResponse(contentType, res, responseMsg);
} else {
// No content, so just indicate accepted
res.setStatus(202);
}
if (isDebug) {
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()));
}
}
/**
* 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);
if (isDebug) {
log.debug("NO AXIS MESSAGE TO RETURN!");
//String resp = Messages.getMessage("noData00");
//res.setContentLength((int) resp.getBytes().length);
//res.getWriter().print(resp);
}
} else {
if (isDebug) {
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());
} 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
*/
private MessageContext createMessageContext(AxisEngine engine,
HttpServletRequest req,
HttpServletResponse res) {
MessageContext msgContext = new MessageContext(engine);
String requestPath = getRequestPath(req);
if (isDebug) {
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:" + requestPath);
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, requestPath);
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(requestPath);
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);
if (soapAction == null) {
String contentType = req.getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
if(contentType != null) {
int index = contentType.indexOf("action");
if(index != -1){
soapAction = contentType.substring(index + 7);
}
}
}
if (isDebug) {
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);
exceptionLog.error(Messages.getMessage("genFault00"), af);
throw af;
}
// the SOAP 1.1 spec & WS-I 1.0 says:
// soapaction = "SOAPAction" ":" [ <"> URI-reference <"> ]
// some implementations leave off the quotes
// we strip them if they are present
if (soapAction.startsWith("\"") && soapAction.endsWith("\"")
&& soapAction.length() >= 2) {
int end = soapAction.length() - 1;
soapAction = soapAction.substring(1, end);
}
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";
}
/**
* Initialize a Handler for the transport defined in the Axis server config.
* This includes optionally filling in query string handlers.
*/
public void initQueryStringHandlers() {
try {
this.transport = getEngine().getTransport(this.transportName);
if (this.transport == null) {
// No transport by this name is defined. Therefore, fill in default
// query string handlers.
this.transport = new SimpleTargetedChain();
this.transport.setOption("qs.list",
"org.apache.axis.transport.http.QSListHandler");
this.transport.setOption("qs.method",
"org.apache.axis.transport.http.QSMethodHandler");
this.transport.setOption("qs.wsdl",
"org.apache.axis.transport.http.QSWSDLHandler");
return;
}
else {
// See if we should use the default query string handlers.
// By default, set this to true (for backwards compatibility).
boolean defaultQueryStrings = true;
String useDefaults = (String)this.transport.getOption(
"useDefaultQueryStrings");
if ((useDefaults != null) &&
useDefaults.toLowerCase().equals("false")) {
defaultQueryStrings = false;
}
if (defaultQueryStrings == true) {
// We should use defaults, so fill them in.
this.transport.setOption("qs.list",
"org.apache.axis.transport.http.QSListHandler");
this.transport.setOption("qs.method",
"org.apache.axis.transport.http.QSMethodHandler");
this.transport.setOption("qs.wsdl",
"org.apache.axis.transport.http.QSWSDLHandler");
}
}
}
catch (AxisFault e) {
// Some sort of problem occurred, let's just make a default transport.
this.transport = new SimpleTargetedChain();
this.transport.setOption("qs.list",
"org.apache.axis.transport.http.QSListHandler");
this.transport.setOption("qs.method",
"org.apache.axis.transport.http.QSMethodHandler");
this.transport.setOption("qs.wsdl",
"org.apache.axis.transport.http.QSWSDLHandler");
return;
}
}
/**
* Attempts to invoke a plugin for the query string supplied in the URL.
*
* @param request the servlet's HttpServletRequest object.
* @param response the servlet's HttpServletResponse object.
* @param writer the servlet's PrintWriter object.
*/
private boolean processQuery(HttpServletRequest request,
HttpServletResponse response,
PrintWriter writer) throws AxisFault {
// Attempt to instantiate a plug-in handler class for the query string
// handler classes defined in the HTTP transport.
String path = request.getServletPath();
String queryString = request.getQueryString();
String serviceName;
AxisEngine engine = getEngine();
Iterator i = this.transport.getOptions().keySet().iterator();
if (queryString == null) {
return false;
}
String servletURI = request.getContextPath() + path;
String reqURI = request.getRequestURI();
// chop off '/'.
if (servletURI.length() + 1 < reqURI.length()) {
serviceName = reqURI.substring(servletURI.length() + 1);
} else {
serviceName = "";
} while (i.hasNext() == true) {
String queryHandler = (String) i.next();
if (queryHandler.startsWith("qs.") == true) {
// Only attempt to match the query string with transport
// parameters prefixed with "qs:".
String handlerName = queryHandler.substring
(queryHandler.indexOf(".") + 1).
toLowerCase();
// Determine the name of the plugin to invoke by using all text
// in the query string up to the first occurence of &, =, or the
// whole string if neither is present.
int length = 0;
boolean firstParamFound = false;
while (firstParamFound == false && length < queryString.length()) {
char ch = queryString.charAt(length++);
if (ch == '&' || ch == '=') {
firstParamFound = true;
--length;
}
}
if (length < queryString.length()) {
queryString = queryString.substring(0, length);
}
if (queryString.toLowerCase().equals(handlerName) == true) {
// Query string matches a defined query string handler name.
// If the defined class name for this query string handler is blank,
// just return (the handler is "turned off" in effect).
if (this.transport.getOption(queryHandler).equals("")) {
return false;
}
try {
// Attempt to dynamically load the query string handler
// and its "invoke" method.
MessageContext msgContext = createMessageContext(engine,
request, response);
Class plugin = Class.forName((String)this.transport.
getOption(queryHandler));
Method pluginMethod = plugin.getDeclaredMethod("invoke",
new Class[] {msgContext.getClass()});
String url = HttpUtils.getRequestURL(request).toString();
// Place various useful servlet-related objects in
// the MessageContext object being delivered to the
// plugin.
msgContext.setProperty(MessageContext.TRANS_URL, url);
msgContext.setProperty(HTTPConstants.
PLUGIN_SERVICE_NAME, serviceName);
msgContext.setProperty(HTTPConstants.PLUGIN_NAME,
handlerName);
msgContext.setProperty(HTTPConstants.
PLUGIN_IS_DEVELOPMENT,
new Boolean(isDevelopment()));
msgContext.setProperty(HTTPConstants.PLUGIN_ENABLE_LIST,
new Boolean(enableList));
msgContext.setProperty(HTTPConstants.PLUGIN_ENGINE,
engine);
msgContext.setProperty(HTTPConstants.PLUGIN_WRITER,
writer);
msgContext.setProperty(HTTPConstants.PLUGIN_LOG, log);
msgContext.setProperty(HTTPConstants.
PLUGIN_EXCEPTION_LOG,
exceptionLog);
// Invoke the plugin.
pluginMethod.invoke(plugin.newInstance(),
new Object[] {msgContext});
writer.close();
return true;
} catch (InvocationTargetException ie) {
reportTroubleInGet(ie.getTargetException(), response,
writer);
// return true to prevent any further processing
return true;
} catch (Exception e) {
reportTroubleInGet(e, response, writer);
// return true to prevent any further processing
return true;
}
}
}
}
return false;
}
/**
* getRequestPath a returns request path for web service padded with
* request.getPathInfo for web services served from /services directory.
* This is a required to support serving .jws web services from /services
* URL. See AXIS-843 for more information.
*
* @param request HttpServletRequest
* @return String
*/
private static String getRequestPath(HttpServletRequest request) {
return request.getServletPath() + ((request.getPathInfo() != null) ?
request.getPathInfo() : "");
}
}