/*
* Copyright 2003 Federal Chancellery Austria
* MOA-SPSS has been developed in a cooperation between BRZ, the Federal
* Chancellery Austria - ICT staff unit, and Graz University of Technology.
*
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved by
* the European Commission - subsequent versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
* http://www.osor.eu/eupl/
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the Licence is distributed on an "AS IS" basis,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Licence for the specific language governing permissions and
* limitations under the Licence.
*
* This product combines work with different licenses. See the "NOTICE" text
* file for details on the various modules and licenses.
* The "NOTICE" text file is part of the distribution. Any derivative works
* that you distribute must include a readable copy of the "NOTICE" text file.
*/
package at.gv.egovernment.moa.spss.server.service;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.Iterator;
import javax.servlet.http.HttpServletRequest;
import org.apache.axis.AxisFault;
import org.apache.axis.Message;
import org.apache.axis.MessageContext;
import org.apache.axis.attachments.AttachmentPart;
import org.apache.axis.handlers.BasicHandler;
import org.apache.axis.transport.http.HTTPConstants;
import org.apache.axis.utils.Messages;
import org.apache.axis.utils.XMLUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import at.gv.egovernment.moa.spss.MOASystemException;
import at.gv.egovernment.moa.spss.server.config.ConfigurationProvider;
import at.gv.egovernment.moa.spss.server.transaction.TransactionContext;
import at.gv.egovernment.moa.spss.server.transaction.TransactionContextManager;
import at.gv.egovernment.moa.spss.server.transaction.TransactionIDGenerator;
import at.gv.egovernment.moa.spss.util.MessageProvider;
import at.gv.egovernment.moaspss.logging.LogMsg;
import at.gv.egovernment.moaspss.logging.Logger;
import at.gv.egovernment.moaspss.logging.LoggingContext;
import at.gv.egovernment.moaspss.logging.LoggingContextManager;
import at.gv.egovernment.moaspss.util.DOMUtils;
/**
* An handler that is invoked on each web service request and performs some
* central message handling.
*
* Mainly sets up the TransactionContext
for the current
* transaction (i.e. web service request).
*
* @author Patrick Peck
* @author Stefan Knirsch
* @version $Id$
*/
public class AxisHandler extends BasicHandler {
/**
*
*/
private static final long serialVersionUID = 2520698947819506866L;
/** The resource names of the messages to load. */
private static final String MOA_SPSS_WSDL_RESOURCE_ = "/resources/wsdl/MOA-SPSS-2.0.0.wsdl";
/** The property name for accessing the HTTP request. */
private static final String REQUEST_PROPERTY = HTTPConstants.MC_HTTP_SERVLETREQUEST;
/** The property name for accessing the X509 client certificate chain. */
private static final String X509_CERTIFICATE_PROPERTY = "javax.servlet.request.X509Certificate";
/** The property name for accessing the SOAP action header. */
private static final String SOAP_ACTION_HEADER = "soapaction";
/** URI of the SOAP XML namespace. */
public static final String SOAP_NS_URI = "http://schemas.xmlsoap.org/soap/envelope/";
/** Prefix used for the SOAP XML namespace */
public static final String SOAP_PREFIX = "soapenv";
/** Simple string contains the front part of the enveloping SOAP wrapping */
private static final String SOAP_PART_PRE =
"";
/** Simple string contains the post part of the enveloping SOAP wrapping */
private static final String SOAP_PART_POST = "";
/**
* Handle an invocation of this handler.
*
* @param msgContext Information about this request/response.
* @throws AxisFault An error occurred during processing of the request.
* @see org.apache.axis.Handler#invoke(MessageContext)
*/
@Override
public void invoke(MessageContext msgContext) throws AxisFault {
if (!msgContext.getPastPivot()) {
handleRequest(msgContext);
} else {
handleResponse(msgContext);
}
}
/**
* This method is called by invoke
to handle incoming requests.
*
* @param msgContext The context as provided to invoke
.
* @throws AxisFault An error occurred during processing of the request.
*/
private void handleRequest(MessageContext msgContext) throws AxisFault {
Message soapMessage = null;
try {
Logger.trace("---- Entering Axishandler");
// generate a unique transaction id and build the TransactionContext
// for this request
final HttpServletRequest request = (HttpServletRequest) msgContext.getProperty(REQUEST_PROPERTY);
final X509Certificate[] clientCert = (X509Certificate[]) request.getAttribute(
X509_CERTIFICATE_PROPERTY);
if (Logger.isTraceEnabled()) {
final Enumeration allHeaders = request.getHeaderNames();
if (allHeaders != null) {
while (allHeaders.hasMoreElements()) {
final String header = allHeaders.nextElement();
Logger.trace("Header: " + header + " : " + request.getHeader(header));
}
}
}
// Configure Axis
// AxisProperties.setProperty(AxisEngine.PROP_ENABLE_NAMESPACE_PREFIX_OPTIMIZATION,"false");
// AxisProperties.setProperty(AxisEngine.PROP_DOMULTIREFS,"false");
// AxisProperties.setProperty(AxisEngine.PROP_SEND_XSI,"true");
// msgContext.setProperty(org.apache.axis.SOAPPart.ALLOW_FORM_OPTIMIZATION,
// Boolean.FALSE);
// msgContext.setProperty(org.apache.axis.
// AxisEngine.PROP_ENABLE_NAMESPACE_PREFIX_OPTIMIZATION,"false");
// AxisProperties.setProperty(AxisEngine.PROP_ATTACHMENT_IMPLEMENTATION,
// AxisEngine.DEFAULT_ATTACHMENT_IMPL);
soapMessage = msgContext.getCurrentMessage();
final ConfigurationProvider configuration = ConfigurationProvider.getInstance();
new String(soapMessage.getSOAPPartAsBytes());
Element xmlRequest = null;
// log.info(soapMessage.getSOAPPartAsString());
final Element soapPart = DOMUtils
.parseDocument(new ByteArrayInputStream(soapMessage.getSOAPPartAsBytes()), false, null, null)
.getDocumentElement();
if (soapPart != null) {
// TODO: check if DOM Version is intolerant when white spaces
// are between tags (preceding normalization would be necessary)
final NodeList soapBodies = soapPart.getElementsByTagNameNS(SOAP_NS_URI, "Body");
if (soapBodies != null && soapBodies.getLength() > 0) {
xmlRequest = DOMUtils.getElementFromNodeList(soapBodies.item(0).getChildNodes());
}
// oder TODO: Evaluierung ob XPATH schneller
/*
* HashMap nSMap = new HashMap(); nSMap.put((String)SOAP_PREFIX, SOAP_NS_URI);
* Element soapBody = (Element) XPathUtils.selectSingleNode(soapPart, nSMap,
* "/"+SOAP_PREFIX+":Envelope/"+SOAP_PREFIX+":Body"); if (soapBody!=null) {
* xmlRequest = DOMUtils.getElementFromNodeList(soapBody.getChildNodes()); }
*/
}
final TransactionContext context = new TransactionContext(TransactionIDGenerator.nextID(), clientCert,
configuration, xmlRequest, null);
String soapAction = request.getHeader(SOAP_ACTION_HEADER);
if ("\"\"".equals(soapAction)) {
// if http soap action header is empty
soapAction = msgContext.getTargetService();
}
context.setRequestName(soapAction);
if (soapMessage.getAttachmentsImpl() != null) {
Logger.info("Attachments is NOT null!");
Logger.trace(">>> Get AttachmentCount");
final int attachmentCount = soapMessage.getAttachmentsImpl().getAttachmentCount();
Logger.trace("<<< Finished Get AttachmentCount");
if (attachmentCount > 0) {
// add SOAP attachments to transaction context
@SuppressWarnings("rawtypes")
final Iterator iterator = soapMessage.getAttachments();
while (iterator.hasNext()) {
final AttachmentPart attachment = (AttachmentPart) iterator.next();
final String id = attachment.getContentId();
final String type = attachment.getContentType();
// Now get the InputStream (note: we could also get the
// content with Object content =
// attachment.getContent();)
InputStream is = null;
final javax.activation.DataHandler datahandler = attachment.getDataHandler();
final int TYPE = 2;
switch (TYPE) {
case 1: {
final org.apache.axis.attachments.ManagedMemoryDataSource mmds =
(org.apache.axis.attachments.ManagedMemoryDataSource) datahandler
.getDataSource();
context.addAttachment(id, type, mmds);
break;
}
case 2: {
is = datahandler.getDataSource().getInputStream();
context.addAttachment(id, type, is, datahandler.getDataSource().getName());
break;
}
}
debug("handler.06", new Object[] { id, type });
}
}
} else {
Logger.info("Attachments is null!");
}
setUpContexts(context);
// log some information about the request
info("handler.00", new Object[] { context.getTransactionID(), msgContext.getTargetService() });
info("handler.01", new Object[] { request.getRemoteAddr() });
if (clientCert != null) {
info("handler.02", new Object[] { clientCert[0].getSubjectDN(), clientCert[0].getSerialNumber(),
clientCert[0].getIssuerDN() });
} else {
info("handler.03", null);
}
if (Logger.isTraceEnabled()) {
// OutputFormat format = new OutputFormat((Document)
// xmlRequest.getOwnerDocument());
// format.setLineSeparator("\n");
// format.setIndenting(false);
// format.setPreserveSpace(true);
// format.setOmitXMLDeclaration(false);
// format.setEncoding("UTF-8");
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
// XMLSerializer conSerializer = new XMLSerializer(baos,
// format);
// conSerializer.serialize(xmlRequest);
// Logger.debug(new LogMsg("Request:" + baos.toString()));
final String msg = soapMessage.getSOAPPartAsString();
Logger.trace(new LogMsg(msg));
}
} catch (final MOASystemException e) {
final MOASystemException se = new MOASystemException("2900", null, e);
final AxisFault fault = AxisFault.makeFault(se);
fault.setFaultDetail(new Element[] { se.toErrorResponse() });
throw fault;
} catch (final SAXException t) {
if (soapMessage != null && soapMessage.getSOAPPartAsBytes() != null) {
try {
Logger.debug("Req: " + new String(soapMessage.getSOAPPartAsBytes(), "UTF-8"));
} catch (final UnsupportedEncodingException e1) {
e1.printStackTrace();
}
}
t.printStackTrace();
Logger.info(new LogMsg(t.getStackTrace()));
final MOASystemException e = new MOASystemException("2900", null, t);
final AxisFault fault = AxisFault.makeFault(e);
fault.setFaultDetail(new Element[] { e.toErrorResponse() });
throw fault;
} catch (final Throwable t) {
t.printStackTrace();
Logger.info(new LogMsg(t.getStackTrace()));
final MOASystemException e = new MOASystemException("2900", null, t);
final AxisFault fault = AxisFault.makeFault(e);
fault.setFaultDetail(new Element[] { e.toErrorResponse() });
throw fault;
}
Logger.trace("---- Leaving Axishandler");
}
/**
* This method is called by invoke
to handle outgoing responses.
*
* @param msgContext The context as provided to invoke
.
* @throws AxisFault An error occurred during processing of the response.
*/
private void handleResponse(MessageContext msgContext) throws AxisFault {
String xmlResponseString = null;
String soapResponseString = null;
final TransactionContext context = TransactionContextManager.getInstance().getTransactionContext();
final Element xmlResponse = context.getResponse();
if (xmlResponse != null) {
try {
xmlResponseString = DOMUtils.serializeNode(xmlResponse, true);
/*
* Soll die Antwort nur \n enthalten, so gibt es 2 Möglichkeiten: 1.) Xalan
* Version und xmlResponseString = DOMUtils.serializeNode(xmlResponse, true,
* "\n"); 2.) OutputFormat serializerFormat = new OutputFormat((Document)
* xmlResponse.getOwnerDocument()); serializerFormat.setLineSeparator("\n");
* serializerFormat.setIndenting(false);
* serializerFormat.setPreserveSpace(true);
* serializerFormat.setOmitXMLDeclaration(true);
* serializerFormat.setEncoding("UTF-8"); ByteArrayOutputStream serializedBytes
* = new ByteArrayOutputStream(); XMLSerializer serializer = new
* XMLSerializer(serializedBytes, serializerFormat);
* serializer.serialize(xmlResponse); serializedBytes.close(); xmlResponseString
* = serializedBytes.toString("UTF-8");
*/
if (Logger.isTraceEnabled()) {
Logger.trace(new LogMsg(xmlResponseString));
}
soapResponseString = SOAP_PART_PRE + xmlResponseString + SOAP_PART_POST;
// override axis response-message
msgContext.setResponseMessage(new Message(soapResponseString));
} catch (final Throwable t) {
t.printStackTrace();
Logger.info(new LogMsg(t.getStackTrace()));
final MOASystemException e = new MOASystemException("2900", null, t);
final AxisFault fault = AxisFault.makeFault(e);
fault.setFaultDetail(new Element[] { e.toErrorResponse() });
throw fault;
}
} else {
// Fallback: if functions do not set the resulting response in the
// transaction, the original one from axis will be used
soapResponseString = msgContext.getCurrentMessage().getSOAPPartAsString();
}
info("handler.04", null);
if (Logger.isDebugEnabled()) {
Logger.debug(new LogMsg(soapResponseString));
}
tearDownContexts();
}
/**
* Called, when the processing of the web service fails.
*
* @param msgContext Information about the current request.
* @see org.apache.axis.Handler#onFault(org.apache.axis.MessageContext)
*/
@Override
public void onFault(MessageContext msgContext) {
info("handler.05", null);
tearDownContexts();
}
/**
* Set up the thread-local contexts (TransactionContext
and
* LoggingContext
).
*
* @param context The TransactionContext
to set for the current
* request.
*/
private void setUpContexts(TransactionContext context) {
// set the transaction context in the TransactionContextManager
final TransactionContextManager tcm = TransactionContextManager.getInstance();
tcm.setTransactionContext(context);
// set the logging context in the LoggingContextManager
final LoggingContextManager lcm = LoggingContextManager.getInstance();
final LoggingContext lc = new LoggingContext(context.getTransactionID());
lcm.setLoggingContext(lc);
}
/**
* Tear down the thread-local contexts.
*/
private void tearDownContexts() {
final TransactionContextManager tcm = TransactionContextManager.getInstance();
// delete temporary files
final TransactionContext context = tcm.getTransactionContext();
context.cleanAttachmentCache();
// unset the transaction context
tcm.setTransactionContext(null);
// unset the logging context
final LoggingContextManager lcm = LoggingContextManager.getInstance();
lcm.setLoggingContext(null);
}
/**
* Generate the WSDL into the msgContext
.
*
* The code of this method is more or less copied from the
* org.apache.axis.handlers.soap.SOAPService
class contained in the
* 1.1 release of Axis to allow for a missing wsdlFile
(so that a
* resource by the same name is searched for in the classpath). The
* implementation of this method should be obsolete if Axis 1.1 or higher is
* used.
*
* @param msgContext The MessageContext
that will contain the WSDL
* description of the current web service.
* @throws AxisFault An error occurred producing the WSDL.
*/
@Override
public void generateWSDL(MessageContext msgContext) throws AxisFault {
InputStream instream = null;
try {
final String filename = MOA_SPSS_WSDL_RESOURCE_;
final File file = new File(filename);
if (file.exists()) {
// if this resolves to a file, load it
instream = new FileInputStream(filename);
} else {
// else load a named resource in our classloader.
instream = this.getClass().getResourceAsStream(filename);
if (instream == null) {
final String errorText = Messages.getMessage("wsdlFileMissing", filename);
throw new AxisFault(errorText);
}
}
final Document doc = XMLUtils.newDocument(instream);
msgContext.setProperty("WSDL", doc);
} catch (final Exception e) {
throw AxisFault.makeFault(e);
} finally {
if (instream != null) {
try {
instream.close();
} catch (final IOException e) {
// ok to do nothing here
}
}
}
}
/**
* Utility function to issue an info message to the log.
*
* @param messageId The ID of the message to log.
* @param parameters Additional message parameters.
*/
private static void info(String messageId, Object[] parameters) {
final MessageProvider msg = MessageProvider.getInstance();
Logger.info(new LogMsg(msg.getMessage(messageId, parameters)));
}
/**
* Utility function to issue an debug message to the log.
*
* @param messageId The ID of the message to log.
* @param parameters Additional message parameters.
*/
private static void debug(String messageId, Object[] parameters) {
final MessageProvider msg = MessageProvider.getInstance();
Logger.debug(new LogMsg(msg.getMessage(messageId, parameters)));
}
// private byte[] toByteArray(AttachmentPart attachment) throws
// SOAPException, IOException
// {
// ByteArrayOutputStream outputStream = new
// ByteArrayOutputStream(attachment.getSize());
// InputStream inputStream = (InputStream) attachment.getContent();
// int currentByte = -1;
// while ((currentByte = inputStream.read()) != -1)
// outputStream.write(currentByte);
//
// inputStream.close();
// outputStream.close();
//
// return outputStream.toByteArray();
//
// }
}