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.security.cert.X509Certificate; 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 at.gv.egovernment.moa.logging.LogMsg; import at.gv.egovernment.moa.logging.Logger; import at.gv.egovernment.moa.logging.LoggingContext; import at.gv.egovernment.moa.logging.LoggingContextManager; 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.moa.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 { /** The resource names of the messages to load. */ private static final String MOA_SPSS_WSDL_RESOURCE_ = "/resources/wsdl/MOA-SPSS-1.3.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) */ 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 { try { // generate a unique transaction id and build the TransactionContext // for this request HttpServletRequest request = (HttpServletRequest) msgContext.getProperty(REQUEST_PROPERTY); X509Certificate[] clientCert = (X509Certificate[]) request.getAttribute(X509_CERTIFICATE_PROPERTY); //Configure Axis //msgContext.setProperty(org.apache.axis.SOAPPart.ALLOW_FORM_OPTIMIZATION, Boolean.FALSE); Message soapMessage = msgContext.getCurrentMessage(); ConfigurationProvider configuration = ConfigurationProvider.getInstance(); Element xmlRequest = null; Element soapPart = DOMUtils.parseDocument(new ByteArrayInputStream(soapMessage.getSOAPPartAsBytes()), false, null, null).getDocumentElement(); if (soapPart!=null) { NodeList soapBodies = soapPart.getElementsByTagNameNS(SOAP_NS_URI, "Body"); if (soapBodies!=null && soapBodies.getLength()>0) { xmlRequest = (Element) soapBodies.item(0).getFirstChild(); } //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= (Element) soapBody.getFirstChild(); } */ } TransactionContext context = new TransactionContext( TransactionIDGenerator.nextID(), clientCert, configuration, xmlRequest, null); String soapAction = (String) request.getHeader(SOAP_ACTION_HEADER); if ("\"\"".equals(soapAction)) { // if http soap action header is empty soapAction = msgContext.getTargetService(); } context.setRequestName(soapAction); int attachmentCount = soapMessage.getAttachmentsImpl().getAttachmentCount(); if (attachmentCount>0) { // add SOAP attachments to transaction context Iterator iterator = soapMessage.getAttachments(); while (iterator.hasNext()) { AttachmentPart attachment = (AttachmentPart)iterator.next(); String id = attachment.getContentId(); String type = attachment.getContentType(); //Now get the InputStream (note: we could also get the content with Object content = attachment.getContent();) InputStream is = null; javax.activation.DataHandler datahandler = attachment.getDataHandler(); org.apache.axis.attachments.ManagedMemoryDataSource mmds = (org.apache.axis.attachments.ManagedMemoryDataSource)datahandler.getDataSource(); if (mmds!=null){ is = mmds.getInputStream(); } debug("handler.06", new Object[] {id, type}); context.addAttachment(id, type, mmds); } } 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.isDebugEnabled()) { String msg = soapMessage.getSOAPPartAsString(); Logger.debug(new LogMsg(msg)); } } catch (MOASystemException e) { MOASystemException se = new MOASystemException("2900", null, e); AxisFault fault = AxisFault.makeFault(se); fault.setFaultDetail(new Element[] { se.toErrorResponse()}); throw fault; } catch (Throwable t) { t.printStackTrace(); Logger.info(new LogMsg(t.getStackTrace())); MOASystemException e = new MOASystemException("2900", null, t); AxisFault fault = AxisFault.makeFault(e); fault.setFaultDetail(new Element[] { e.toErrorResponse()}); throw fault; } } /** * 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; TransactionContext context = TransactionContextManager.getInstance().getTransactionContext(); 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.) höhere 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"); */ soapResponseString = SOAP_PART_PRE + xmlResponseString + SOAP_PART_POST; //override axis response-message msgContext.setResponseMessage(new Message(soapResponseString)); } catch (Throwable t) { t.printStackTrace(); Logger.info(new LogMsg(t.getStackTrace())); MOASystemException e = new MOASystemException("2900", null, t); 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) */ 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 TransactionContextManager tcm = TransactionContextManager.getInstance(); tcm.setTransactionContext(context); // set the logging context in the LoggingContextManager LoggingContextManager lcm = LoggingContextManager.getInstance(); LoggingContext lc = new LoggingContext(context.getTransactionID()); lcm.setLoggingContext(lc); } /** * Tear down the thread-local contexts. */ private void tearDownContexts() { TransactionContextManager tcm = TransactionContextManager.getInstance(); //delete temporary files TransactionContext context = tcm.getTransactionContext(); context.cleanAttachmentCache(); // unset the transaction context tcm.setTransactionContext(null); // unset the logging context 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. */ public void generateWSDL(MessageContext msgContext) throws AxisFault { InputStream instream = null; try { String filename = MOA_SPSS_WSDL_RESOURCE_; 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) { String errorText = Messages.getMessage("wsdlFileMissing", filename); throw new AxisFault(errorText); } } Document doc = XMLUtils.newDocument(instream); msgContext.setProperty("WSDL", doc); } catch (Exception e) { throw AxisFault.makeFault(e); } finally { if (instream != null) { try { instream.close(); } catch (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) { 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) { MessageProvider msg = MessageProvider.getInstance(); Logger.debug(new LogMsg(msg.getMessage(messageId, parameters))); } }