/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package de.christofreichardt.jaxws.securitypriceservice;

import de.christofreichardt.diagnosis.AbstractTracer;
import de.christofreichardt.diagnosis.LogLevel;
import de.christofreichardt.diagnosis.Traceable;
import de.christofreichardt.diagnosis.TracerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Set;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.soap.SOAPException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.ws.handler.MessageContext;
import javax.xml.ws.handler.soap.SOAPHandler;
import javax.xml.ws.handler.soap.SOAPMessageContext;
import org.xml.sax.SAXException;

/**
 *
 * @author Christof Reichardt
 */
public class SchemaValidationHandler implements SOAPHandler<SOAPMessageContext>, Traceable {

  @Override
  public Set<QName> getHeaders() {
    return Collections.emptySet();
  }

  @Override
  public boolean handleMessage(SOAPMessageContext soapMessageContext) {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("boolean", this, "handleMessage(SOAPMessageContext soapMessageContext)");

    try {
      boolean outbound = (boolean) soapMessageContext.get(SOAPMessageContext.MESSAGE_OUTBOUND_PROPERTY);
      tracer.out().printfIndentln("outbound = %b", outbound);
      
      if (!outbound) {
        try {
          DOMSource domSource = new DOMSource(soapMessageContext.getMessage().getSOAPBody().getFirstChild());
          Schema schema = retrieveSchema(soapMessageContext);
          Validator validator = schema.newValidator();
          validator.validate(domSource);
        }
        catch (IOException | SAXException | SOAPException ex) {
          tracer.logException(LogLevel.ERROR, ex, getClass(), "handleMessage(SOAPMessageContext soapMessageContext)");
        }
      }
      
      return true;
    }
    finally {
      tracer.wayout();
    }
  }

  @Override
  public boolean handleFault(SOAPMessageContext context) {
    return true;
  }

  @Override
  public void close(MessageContext context) {
  }
  
  private Schema retrieveSchema(SOAPMessageContext soapMessageContext) throws IOException, SAXException {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("Schema", this, "retrieveSchema(SOAPMessageContext soapMessageContext)");

    try {
      if (!soapMessageContext.containsKey("de.christofreichardt.jaxws.securitypriceservice.schema")) {
        SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        try (InputStream inputStream = SchemaValidationHandler.class.getClassLoader().getResourceAsStream("/de/christofreichardt/wsdl/SecurityPriceServiceSchema.xsd")) {
          if (inputStream == null)
            throw new IOException("Cannot access schema via ClassLoader");
          StreamSource streamSource = new StreamSource(inputStream);
          soapMessageContext.put("de.christofreichardt.jaxws.securitypriceservice.schema", schemaFactory.newSchema(streamSource));
        }
      }
      Schema schema = (Schema) soapMessageContext.get("de.christofreichardt.jaxws.securitypriceservice.schema");
      
      return schema;
    }
    finally {
      tracer.wayout();
    }
  }

  @Override
  public AbstractTracer getCurrentTracer() {
    return TracerFactory.getInstance().getCurrentQueueTracer();
  }

}
