package com.evolveum.midpoint.report.impl;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.xml.namespace.QName;
import javax.xml.soap.Detail;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.SOAPFault;
import javax.xml.transform.dom.DOMSource;
import javax.xml.ws.Holder;
import javax.xml.ws.Provider;
import javax.xml.ws.soap.SOAPFaultException;
import com.evolveum.midpoint.schema.util.MiscSchemaUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.SerializationContext;
import com.evolveum.midpoint.prism.SerializationOptions;
import com.evolveum.midpoint.prism.xnode.RootXNode;
import com.evolveum.midpoint.prism.xnode.XNode;
import com.evolveum.midpoint.report.api.ReportPort;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.api_types_3.ObjectListType;
import com.evolveum.midpoint.xml.ns._public.common.audit_3.AuditEventRecordListType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType;
import com.evolveum.midpoint.xml.ns._public.common.fault_3.FaultMessage;
import com.evolveum.midpoint.xml.ns._public.report.report_3.EvaluateAuditScriptResponseType;
import com.evolveum.midpoint.xml.ns._public.report.report_3.EvaluateAuditScriptType;
import com.evolveum.midpoint.xml.ns._public.report.report_3.EvaluateScriptResponseType;
import com.evolveum.midpoint.xml.ns._public.report.report_3.EvaluateScriptType;
import com.evolveum.midpoint.xml.ns._public.report.report_3.ProcessReportResponseType;
import com.evolveum.midpoint.xml.ns._public.report.report_3.ProcessReportType;
@Service
public class ReportWebServiceRaw implements Provider<DOMSource> {
private static transient Trace LOGGER = TraceManager.getTrace(ReportWebService.class);
public static final String NS_SOAP11_ENV = "http://schemas.xmlsoap.org/soap/envelope/";
public static final String NS_SOAP11_ENV_PREFIX = "SOAP-ENV";
public static final QName SOAP11_FAULT = new QName(NS_SOAP11_ENV, "Fault");
public static final QName SOAP11_FAULTCODE = new QName("", "faultcode");
public static final String SOAP11_FAULTCODE_SERVER = NS_SOAP11_ENV_PREFIX + ":Server";
public static final QName SOAP11_FAULTSTRING = new QName("", "faultstring");
public static final QName SOAP11_FAULTACTOR = new QName("", "faultactor");
public static final QName SOAP11_FAULT_DETAIL = new QName("", "detail");
public static final String ACTOR = "TODO";
@Autowired(required = true)
private PrismContext prismContext;
@Autowired(required = true)
private ReportWebService reportService;
@Override
public DOMSource invoke(DOMSource request) {
try {
return invokeAllowingFaults(request);
} catch (FaultMessage faultMessage) {
try {
SOAPFactory factory = SOAPFactory.newInstance();
SOAPFault soapFault = factory.createFault();
soapFault.setFaultCode(SOAP11_FAULTCODE_SERVER); // todo here is a constant until we have a mechanism to determine the correct value (client / server)
soapFault.setFaultString(faultMessage.getMessage());
Detail detail = soapFault.addDetail();
serializeFaultMessage(detail, faultMessage);
// fault actor?
// stack trace of the outer exception (FaultMessage) is unimportant, because it is always created at one place
// todo consider providing stack trace of the inner exception
//Detail detail = soapFault.addDetail();
//detail.setTextContent(getStackTraceAsString(faultMessage));
throw new SOAPFaultException(soapFault);
} catch (SOAPException e) {
throw new RuntimeException("SOAP Exception: " + e.getMessage(), e);
}
}
}
public DOMSource invokeAllowingFaults(DOMSource request) throws FaultMessage {
Node rootNode = request.getNode();
Element rootElement;
if (rootNode instanceof Document) {
rootElement = ((Document) rootNode).getDocumentElement();
} else if (rootNode instanceof Element) {
rootElement = (Element) rootNode;
} else {
// throw ws.createIllegalArgumentFault("Unexpected DOM node type: " + rootNode);
throw new FaultMessage("Unexpected DOM node type: " + rootNode);
}
Object requestObject;
try {
requestObject = prismContext.parserFor(rootElement).parseRealValue();
} catch (SchemaException e) {
throw new FaultMessage("Couldn't parse SOAP request body because of schema exception: " + e.getMessage());
// throw ws.createIllegalArgumentFault("Couldn't parse SOAP request body because of schema exception: " + e.getMessage());
}
Node response;
Holder<OperationResultType> operationResultTypeHolder = new Holder<>();
SerializationContext ctx= new SerializationContext(SerializationOptions.createSerializeReferenceNames());
try {
if (requestObject instanceof EvaluateScriptType){
EvaluateScriptType s = (EvaluateScriptType) requestObject;
ObjectListType olt = reportService.evaluateScript(s.getScript(), s.getParameters());
EvaluateScriptResponseType sr = new EvaluateScriptResponseType();
sr.setObjectList(olt);
response = prismContext.domSerializer().context(ctx).serializeAnyData(sr, ReportPort.EVALUATE_SCRIPT_RESPONSE);
} else if (requestObject instanceof EvaluateAuditScriptType){
EvaluateAuditScriptType s = (EvaluateAuditScriptType) requestObject;
AuditEventRecordListType olt = reportService.evaluateAuditScript(s.getScript(), s.getParameters());
EvaluateAuditScriptResponseType sr = new EvaluateAuditScriptResponseType();
sr.setObjectList(olt);
response = prismContext.domSerializer().context(ctx).serializeAnyData(sr, ReportPort.EVALUATE_AUDIT_SCRIPT_RESPONSE);
} else if (requestObject instanceof ProcessReportType){
ProcessReportType p = (ProcessReportType) requestObject;
ObjectListType olt = reportService.processReport(p.getQuery(), p.getParameters(), p.getOptions());
ProcessReportResponseType pr = new ProcessReportResponseType();
pr.setObjectList(olt);
response = prismContext.domSerializer().context(ctx).serializeAnyData(pr, ReportPort.PROCESS_REPORT_RESPONSE);
} else {
throw new FaultMessage("Unsupported request type: " + requestObject);
}
} catch (SchemaException e) {
throwFault(e, operationResultTypeHolder.value);
// not reached
return null;
}
// brutal hack for MID-2001 (serializing and parsing eliminates the problem!)
//String serialized = DOMUtil.printDom(response).toString();
//LOGGER.trace("WEB SERVICE RESPONSE:\n{}", serialized);
//response = DOMUtil.parseDocument(serialized);
return new DOMSource(response);
}
private void serializeFaultMessage(Detail detail, FaultMessage faultMessage) {
MiscSchemaUtil.serializeFaultMessage(detail, faultMessage, prismContext, LOGGER);
}
// private DOMSource serializeFaultMessage(FaultMessage faultMessage) {
// Element faultElement = DOMUtil.createElement(SOAP11_FAULT);
// Element faultCodeElement = DOMUtil.createSubElement(faultElement, SOAP11_FAULTCODE);
// faultCodeElement.setTextContent(SOAP11_FAULTCODE_SERVER); // todo here is a constant until we have a mechanism to determine the correct value (client / server)
// Element faultStringElement = DOMUtil.createSubElement(faultElement, SOAP11_FAULTSTRING);
// faultStringElement.setTextContent(faultMessage.getMessage());
// Element faultActorElement = DOMUtil.createSubElement(faultElement, SOAP11_FAULTACTOR);
// faultActorElement.setTextContent("TODO"); // todo
// Element faultDetailElement = DOMUtil.createSubElement(faultElement, SOAP11_FAULT_DETAIL);
// faultDetailElement.setTextContent(getStackTraceAsString(faultMessage));
// return new DOMSource(faultElement.getOwnerDocument());
// }
private String getStackTraceAsString(FaultMessage faultMessage) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
faultMessage.printStackTrace(pw);
pw.close();
return sw.toString();
}
private void throwFault(Exception ex, OperationResultType resultType) throws FaultMessage {
if (resultType != null) {
throw new FaultMessage(ex.getMessage());
// ws.throwFault(ex, OperationResult.createOperationResult(resultType));
} else {
throw new FaultMessage(ex.getMessage());
// ws.throwFault(ex, null);
}
}
}