
package de.ivu.elect.business.system.control;

import java.io.ByteArrayOutputStream;

import javax.xml.soap.SOAPMessage;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.ws.handler.soap.SOAPMessageContext;

public class SoapWebServiceLoggingHandler extends AbstractSoapWebServiceHandler {

  private final Transformer xmlPrettyPrintTransformer;

  public SoapWebServiceLoggingHandler() {
    super();

    this.xmlPrettyPrintTransformer = createXmlPrettyPrintTransformer();
  }

  @Override
  protected boolean handleOutboundMessage(SOAPMessageContext context) {
    try {
      logSOAPMessage(context);
    } catch (Exception ex) {
      throw new RuntimeException(ex);
    }

    return true;
  }

  @Override
  protected boolean handleInboundMessage(SOAPMessageContext context) {
    boolean result = super.handleInboundMessage(context);

    try {
      logSOAPMessage(context);
    } catch (Exception ex) {
      throw new RuntimeException(ex);
    }

    return result;
  }

  private void logSOAPMessage(SOAPMessageContext context) throws Exception {
    if (context != null) {
      SOAPMessage soapMessage = context.getMessage();

      if (soapMessage != null) {
        try {
          logXmlPrettyPrint(soapMessage);
        } catch (Exception ignored) {
          logXmlUnformatted(soapMessage);
        }
      }
    }
  }

  private void logXmlPrettyPrint(SOAPMessage soapMessage) throws Exception {
    if (xmlPrettyPrintTransformer != null) {
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      xmlPrettyPrintTransformer.transform(soapMessage.getSOAPPart().getContent(), new StreamResult(bos));

      logXml(bos);
    } else {
      logXmlUnformatted(soapMessage);
    }
  }

  private void logXmlUnformatted(SOAPMessage soapMessage) throws Exception {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    soapMessage.writeTo(bos);

    logXml(bos);
  }

  private void logXml(ByteArrayOutputStream bos) {
    info(System.lineSeparator() + new String(bos.toByteArray()) + System.lineSeparator());
  }

  private Transformer createXmlPrettyPrintTransformer() {
    try {
      TransformerFactory transformerFactory = TransformerFactory.newInstance();
      transformerFactory.setAttribute("indent-number", 2);

      Transformer transformer = transformerFactory.newTransformer();
      transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
      transformer.setOutputProperty(OutputKeys.INDENT, "yes");

      return transformer;
    } catch (Exception ex) {
      warn(ex);
    }

    return null;
  }
}
