/* * 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.AxisEngine; import org.apache.axis.AxisFault; import org.apache.axis.AxisProperties; 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 org.xml.sax.SAXParseException; 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) */ 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 HttpServletRequest request = (HttpServletRequest) msgContext.getProperty(REQUEST_PROPERTY); X509Certificate[] clientCert = (X509Certificate[]) request.getAttribute(X509_CERTIFICATE_PROPERTY); if (Logger.isTraceEnabled()) { Enumeration allHeaders = request.getHeaderNames(); if (allHeaders != null) { while(allHeaders.hasMoreElements()) { 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(); ConfigurationProvider configuration = ConfigurationProvider.getInstance(); new String(soapMessage.getSOAPPartAsBytes()); Element xmlRequest = null; // log.info(soapMessage.getSOAPPartAsString()); 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) 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()); } */ } 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); if (soapMessage.getAttachmentsImpl() != null) { Logger.info("Attachments is NOT null!"); Logger.trace(">>> Get AttachmentCount"); int attachmentCount = soapMessage.getAttachmentsImpl().getAttachmentCount(); Logger.trace("<<< Finished Get AttachmentCount"); if (attachmentCount > 0) { // add SOAP attachments to transaction context @SuppressWarnings("rawtypes") 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(); int TYPE = 2; switch (TYPE) { case 1: { 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())); String msg = soapMessage.getSOAPPartAsString(); Logger.trace(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 (SAXException t ) { if (soapMessage != null && soapMessage.getSOAPPartAsBytes() != null) { try { Logger.debug("Req: " + new String(soapMessage.getSOAPPartAsBytes(), "UTF-8")); } catch (UnsupportedEncodingException e1) { e1.printStackTrace(); } } 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; } 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; } 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; 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.) 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 (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))); } // 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(); // // } }