<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~~~~~~licensing~~~~~~
  error-handler
  ==========
  Copyright (C) 2020 - 2024 EmDev LLC
  ==========
  You may not use this file except in accordance with the License Terms of the Copyright
  Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
  rights to the Software and any copies are the property of the Copyright Holder. Unless
  it is explicitly allowed the Copyright Holder, the User is prohibited from using the
  Software for commercial purposes to provide services to third parties.
  
  The Copyright Holder hereby declares that the Software is provided on an "AS IS".
  Under no circumstances does the Copyright Holder guarantee or promise that the
  Software provided by him will be suitable or not suitable for the specific purposes
  of the User, that the Software will meet all commercial and personal subjective
  expectations of the User, that the Software will work properly, without technical
  errors, quickly and uninterruptedly.
  
  Under no circumstances shall the Copyright Holder or its Affiliates is not liable
  to the User for any direct or indirect losses of the User, his expenses or actual
  damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
  or damage to data, property, etc.
  ~~~~~~/licensing~~~~~~
  -->

<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
           xsi:schemaLocation="
             http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
             ">

    <cm:property-placeholder persistent-id="ru.entaxy.esb.error" update-strategy="reload">
        <cm:default-properties>
            <cm:property name="error.bus.system.id" value="-1"/>
            <cm:property name="error.bus.system.name" value="esb"/>
            <cm:property name="error.bus.always_at_source" value="false"/>
            <cm:property name="error.description.exception_message" value="true"/>
            <cm:property name="error.queue.name" value="error"/>
            <cm:property name="error.system.name" value="error"/>
            <cm:property name="error.asynch.stacktrace.show" value="true"/>
            <cm:property name="error.synch.stacktrace.show" value="false"/>
            <cm:property name="error.test-route.startup" value="false"/>
        </cm:default-properties>
    </cm:property-placeholder>

    <cm:property-placeholder persistent-id="ru.entaxy.esb.error.code" update-strategy="reload"
                             placeholder-prefix="$errorCode{">
        <cm:default-properties>
            <cm:property name="ru.entaxy.esb.system.common.exception.DefaultException" value="520"/>
        </cm:default-properties>
    </cm:property-placeholder>

    <cm:property-placeholder persistent-id="ru.entaxy.esb.error.text" update-strategy="reload"
                             placeholder-prefix="$errorText{">
        <cm:default-properties>
            <cm:property name="http-520" value="Unknown Error"/>
        </cm:default-properties>
    </cm:property-placeholder>

    <cm:property-placeholder persistent-id="ru.entaxy.esb.error.severity" update-strategy="reload"
                             placeholder-prefix="$errorSeverity{">
        <cm:default-properties>
            <cm:property name="FATAL" value="1"/>
            <cm:property name="ERROR" value="2"/>
            <cm:property name="WARN" value="3"/>
            <cm:property name="INFO" value="4"/>
        </cm:default-properties>
    </cm:property-placeholder>

    <bean id="uuidGenerator" class="java.util.UUID" scope="prototype" factory-method="randomUUID"/>
    <bean id="timestamp" class="ru.entaxy.esb.system.core.common.error.handler.util.Timestamp"/>

    <reference id="entaxy-broker" interface="org.apache.camel.Component"
               filter="(connection.name=entaxy-broker)"/>

    <!--    <bean id="errorProcessor" class="ErrorProcessor"/>-->

    <camelContext id="common-error-handler-context" errorHandlerRef="errorHandlerFailed"
                  xmlns="http://camel.apache.org/schema/blueprint"
                  xsi:schemaLocation="http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint-3.4.5.xsd">

        <!--        <errorHandler id="synchronousErrorHandler" redeliveryPolicyRef="noRedelivery"-->
        <!--                      onPrepareFailureRef="errorProcessor"-->
        <!--                      type="DeadLetterChannel" deadLetterUri="direct:syncErrorHandlerEndpoint" />-->
        <errorHandler id="commonErrorHandler" redeliveryPolicyRef="noRedelivery"
                      type="DeadLetterChannel" deadLetterUri="direct-vm:commonErrorEndpoint"/>
        <errorHandler id="errorHandlerFailed" redeliveryPolicyRef="noRedelivery"
                      type="DeadLetterChannel" deadLetterUri="direct:errorHandlerFailedEndpoint"/>
        <errorHandler id="loggingErrorHandler" redeliveryPolicyRef="noRedelivery"/>
        <!--                      type="LoggingErrorHandler" logName="errorHandlerCrash"/>-->
        <redeliveryPolicyProfile id="noRedelivery" disableRedelivery="true"/>

        <route id="error-handler-test-route" errorHandlerRef="commonErrorHandler"
               autoStartup="{{error.test-route.startup}}">
            <from uri="timer:test-route?repeatCount=1"/>
            <log message="error handler test route started successfully\n\n" loggingLevel="INFO"/>

            <setBody>
                <simple>
                    <![CDATA[<packet xmlns="http://www.entaxy.ru/ExchangeTypes/1.0"><content>Oops... some test error occurred</content></packet>]]>
                </simple>
            </setBody>

            <throwException exceptionType="java.lang.IllegalArgumentException" message="Test exception thrown"/>
        </route>

        <!-- the route is designed to handle errors that occurred in other routes of this context  -->
        <route id="error-handler-failed-route" errorHandlerRef="loggingErrorHandler">
            <from uri="direct:errorHandlerFailedEndpoint"/>
            <log message="Exception handled by ${routeId}: ${exception}
                          ${messageHistory}${exception.stacktrace}" loggingLevel="ERROR"/>
            <!--            TODO connect to alarm or monitoring system (send email, sms, etc) -->
        </route>

        <route id="cxf-error-handler-route">
            <from uri="direct-vm:cxfErrorEndpoint"/>
            <log message="Exception handled by ${routeId}: ${header.NTX_ERROR_HANDLER_SoapFault}" loggingLevel="ERROR"/>
            <doTry>
                <to uri="direct-vm:commonErrorEndpoint"/>
                <doFinally>
                    <to uri="xslt:xslt/WrapSoapEnvelope.xsl"/>
                </doFinally>
            </doTry>
        </route>

        <route id="common-error-handler-route">
            <from uri="direct-vm:commonErrorEndpoint"/>
            <log message="Exception handled by ${routeId}: ${exception}
                          on message ${headers.ENTAXY_MessageUUID}
                          ${messageHistory}${exception.stacktrace}" loggingLevel="ERROR"/>

            <setBody>
                <constant><![CDATA[<empty/>]]></constant>
            </setBody>
            <to uri="xslt:xslt/DefaultResponse.xsl"/>
            <to uri="direct:prepareResponseCodeAndTextEndpoint"/>

            <wireTap uri="direct:asynchronousErrorEndpoint"/>
            <choice>
            	<when>
                    <simple>${headers.NTX_ERROR_HANDLER_GenerateResponseEndpoint} != null</simple>
                    <toD uri="direct-vm:${headers.NTX_ERROR_HANDLER_GenerateResponseEndpoint}"/>
                </when>
            	<when>
            		<simple>${headers.NTX_ERROR_HANDLER_SYNC_TYPE} == "application/json"</simple>
            		<to uri="direct:synchronousJSONErrorEndpoint"/>
            	</when>
            	<otherwise>
            		<to uri="direct:synchronousErrorEndpoint"/>
            	</otherwise>
            </choice>
            

            <!-- Remove headers before response -->
            <removeHeaders pattern="*"/>
        </route>

        <route id="prepare-response-code-and-text-route">
            <from uri="direct:prepareResponseCodeAndTextEndpoint"/>
            <setHeader name="NTX_ERROR_HANDLER_asynch_Stacktrace">
                <simple>${properties:error.asynch.stacktrace.show}</simple>
            </setHeader>
            <setHeader name="NTX_ERROR_HANDLER_synch_Stacktrace">
                <simple>${properties:error.synch.stacktrace.show}</simple>
            </setHeader>
            <choice>
                <when>
                    <simple>${exception} != null</simple>
                    <setHeader name="NTX_ERROR_HANDLER_ExceptionClass">
                        <simple resultType="String">${exception.class.name}</simple>
                    </setHeader>
                    <setHeader name="NTX_ERROR_HANDLER_ExceptionMessage">
                        <simple resultType="String">${exception.message}</simple>
                    </setHeader>
                    <when>
	                    <simple>
	                        ${header.NTX_ERROR_HANDLER_asynch_Stacktrace} == "true" || ${header.NTX_ERROR_HANDLER_synch_Stacktrace} == "true"
	                    </simple>
	                    <setHeader name="NTX_ERROR_HANDLER_Stacktrace">
	                        <simple>${exception.stacktrace}</simple>
	                    </setHeader>
	                </when>
                </when>
                <when>
                    <simple>${header.NTX_ERROR_HANDLER_SoapFault} != null</simple>
                    <setHeader name="NTX_ERROR_HANDLER_ExceptionClass">
                        <simple resultType="String">${header.NTX_ERROR_HANDLER_SoapFault.cause.class.name}</simple>
                    </setHeader>
                    <setHeader name="NTX_ERROR_HANDLER_ExceptionMessage">
                        <simple resultType="String">${header.NTX_ERROR_HANDLER_SoapFault.cause.message}</simple>
                    </setHeader>
                    <when>
	                    <simple>
	                        ${header.NTX_ERROR_HANDLER_asynch_Stacktrace} == "true" || ${header.NTX_ERROR_HANDLER_synch_Stacktrace} == "true"
	                    </simple>
	                    <setProperty name="bodyHolder">
	                    	<simple>${body}</simple>
	                    </setProperty>
	                    <bean beanType="org.apache.commons.lang3.exception.ExceptionUtils" method="getStackTrace(${header.NTX_ERROR_HANDLER_SoapFault.cause})"/>
	                    <setHeader name="NTX_ERROR_HANDLER_Stacktrace">
							<simple>${body}</simple>
	                    </setHeader>
	                    <setBody>
	                    	<simple>${exchangeProperty.bodyHolder}</simple>
	                    </setBody>
	                </when>
                </when>
                <otherwise>
                    <setHeader name="NTX_ERROR_HANDLER_ExceptionClass">
                        <simple resultType="String">ru.entaxy.esb.system.common.exception.DefaultException</simple>
                    </setHeader>
                    <setHeader name="NTX_ERROR_HANDLER_ExceptionMessage">
                        <simple resultType="String">Something wrong</simple>
                    </setHeader>
                </otherwise>
            </choice>
            <choice>
                <when>
                    <simple>${header.NTX_ERROR_HANDLER_ExceptionClass}</simple>
                    <doTry>
                        <setHeader name="NTX_ERROR_HANDLER_ResponseCode">
                            <simple>${properties:${header.NTX_ERROR_HANDLER_ExceptionClass}}</simple>
                        </setHeader>
                        <doCatch>
                            <exception>java.lang.Exception</exception>
                            <log message="Exception code not set for class: ${header.NTX_ERROR_HANDLER_ExceptionClass}" loggingLevel="WARN"/>
                            <setHeader name="NTX_ERROR_HANDLER_ResponseCode">
                                <simple>${properties:ru.entaxy.esb.system.common.exception.DefaultException}</simple>
                            </setHeader>
                        </doCatch>
                    </doTry>
                </when>
            </choice>
            <choice>
                <when>
                    <simple>${properties:error.description.exception_message} == "true"</simple>
                    <setHeader name="NTX_ERROR_HANDLER_ResponseText">
                        <simple>${header.NTX_ERROR_HANDLER_ExceptionMessage}</simple>
                    </setHeader>
                </when>
                <otherwise>
                    <setHeader name="NTX_ERROR_HANDLER_ResponseText">
                        <simple>${properties:${header.NTX_ERROR_HANDLER_ResponseCode}}</simple>
                    </setHeader>
                </otherwise>
            </choice>

            <log message="${exchangeProperty.CamelExceptionCaught}\n" loggingLevel="DEBUG"/>
            <log message="${routeId}: \n
                 ${header.NTX_ERROR_HANDLER_ExceptionClass} \n
                 ${header.NTX_ERROR_HANDLER_ResponseCode} ${header.NTX_ERROR_HANDLER_ResponseText}"
                 loggingLevel="DEBUG"/>
        </route>

        <route id="asynchronous-error-handler-route">
            <from uri="direct:asynchronousErrorEndpoint"/>
            <log message="${routeId}" loggingLevel="DEBUG"/>

            <to uri="direct:prepareErrorPacketEndpoint"/>
            <removeHeaders pattern="NTX_ERROR_HANDLER_.+"/>

            <!-- Send error packet to central error handling system -->
            <doTry>
                <toD uri="system:${properties:error.system.name}?exchangePattern=InOnly"/>
                <doCatch>
                    <exception>java.lang.Exception</exception>
                    <log message="${exception.message}" loggingLevel="DEBUG"/>
                    <log message="${exception.stacktrace}" loggingLevel="TRACE"/>
                    <toD uri="entaxy-broker:queue:entaxy.${properties:error.queue.name}?exchangePattern=InOnly"/>
                </doCatch>
            </doTry>

            <!-- Send error packet back to the calling system -->
            <choice>
                <when>
                    <simple>${headers.X-SystemName} != "esb"</simple>
                    <doTry>
                        <toD uri="system:${headers.X-SystemName}?exchangePattern=InOnly"/>
                        <doCatch>
                            <exception>java.lang.Exception</exception>
                            <log message="Can't deliver error packet to system: ${headers.X-SystemName}"
                                 loggingLevel="DEBUG"/>
                            <log message="${exception.message}" loggingLevel="ERROR"/>
                            <log message="${exception.stacktrace}" loggingLevel="DEBUG"/>
                        </doCatch>
                    </doTry>
                </when>
            </choice>
        </route>

        <route id="prepare-error-packet-route">
            <from uri="direct:prepareErrorPacketEndpoint"/>
            <log message="${routeId}" loggingLevel="DEBUG"/>
            <to uri="direct:prepareErrorPacketHeadersEndpoint"/>

            <setHeader name="ENTAXY_Source">
                <constant>esb</constant>
            </setHeader>

            <log message="Before transformation \n${body}" loggingLevel="DEBUG"/>
            <to uri="xslt:xslt/UniversalErrorPacket.xsl"/>
            <log message="After transformation \n${body}" loggingLevel="DEBUG"/>

            <setHeader name="ENTAXY_MessageUUID">
                <simple>${bean:uuidGenerator.toString}</simple>
            </setHeader>
            <setHeader name="ENTAXY_MessageType">
                <constant>ВыгрузкаДанныхКаноническийФормат</constant>
            </setHeader>
            <setHeader name="ENTAXY_ContentType">
                <constant>application/xml</constant>
            </setHeader>
            <setHeader name="CamelJms_IntMessageType">
                <constant>String</constant>
            </setHeader>
            <setHeader name="CamelJms_IntContentXsiType">
                <constant>xs:string</constant>
            </setHeader>
        </route>

        <route id="prepare-error-packet-headers-route">
            <from uri="direct:prepareErrorPacketHeadersEndpoint"/>

            <setHeader name="NTX_ERROR_HANDLER_CentralIntegrationDB">
                <simple>${properties:error.system.name}</simple>
            </setHeader>

            <choice>
                <when>
                    <simple>${headers.ENTAXY_Source} != null &amp;&amp; ${headers.ENTAXY_Source} != ""
                    </simple>
                    <setHeader name="NTX_ERROR_HANDLER_Source">
                        <simple>${headers.ENTAXY_Source}</simple>
                    </setHeader>
                    <setHeader name="NTX_ERROR_HANDLER_Destination">
                        <simple>${headers.ENTAXY_Source}</simple>
                    </setHeader>
                </when>
                <when>
                    <simple>${headers.NTX_SystemId} != null &amp;&amp; ${headers.NTX_SystemId} != ""</simple>
                    <setHeader name="NTX_ERROR_HANDLER_Source">
                        <simple>${headers.NTX_SystemId}</simple>
                    </setHeader>
                    <setHeader name="NTX_ERROR_HANDLER_Destination">
                        <simple>${headers.NTX_SystemId}</simple>
                    </setHeader>
                </when>
                <when>
                    <simple>${headers.X-SystemName} != null &amp;&amp; ${headers.X-SystemName} != ""</simple>
                    <setHeader name="NTX_ERROR_HANDLER_Source">
                        <simple>${headers.X-SystemName}</simple>
                    </setHeader>
                    <setHeader name="NTX_ERROR_HANDLER_Destination">
                        <simple>${headers.X-SystemName}</simple>
                    </setHeader>
                </when>
                <otherwise>
                    <setHeader name="NTX_ERROR_HANDLER_Source">
                        <constant>esb</constant>
                    </setHeader>
                    <setHeader name="X-SystemName">
                        <constant>esb</constant>
                    </setHeader>
                </otherwise>
            </choice>
            <choice>
                <when>
                    <simple>${headers.X-SystemName} == null || ${headers.X-SystemName} == ""</simple>
                    <setHeader name="X-SystemName">
                        <constant>esb</constant>
                    </setHeader>
                </when>
            </choice>
            <choice>
                <when>
                    <simple>${headers.X-SystemId} == null || ${headers.X-SystemId} == ""</simple>
                    <setHeader name="X-SystemId">
                        <constant>-1</constant>
                    </setHeader>
                </when>
            </choice>
            <choice>
                <when>
                    <simple>${properties:error.bus.always_at_source} == "true"</simple>
                    <setHeader name="NTX_ERROR_HANDLER_Source">
                        <constant>esb</constant>
                    </setHeader>
                    <setHeader name="X-SystemName">
                        <constant>esb</constant>
                    </setHeader>
                    <setHeader name="X-SystemId">
                        <constant>-1</constant>
                    </setHeader>
                </when>
            </choice>

            <setHeader name="NTX_ERROR_HANDLER_ErrorUUID">
                <simple>${bean:uuidGenerator.toString}</simple>
            </setHeader>
            <log message="Prepare error packet with eventUUID: ${headers.NTX_ERROR_HANDLER_ErrorUUID}"
                 loggingLevel="ERROR"/>
            <setHeader name="NTX_ERROR_HANDLER_Timestamp">
                <simple>${bean:timestamp?method=currentTimeMillis}</simple>
            </setHeader>
            <when>
                <simple>${headers.NTX_ERROR_HANDLER_SeverityLevel} == null</simple>
                <setHeader name="NTX_ERROR_HANDLER_SeverityLevel">
                    <constant>ERROR</constant>
                </setHeader>
            </when>
        </route>

        <route id="synchronous-error-handler-route">
            <from uri="direct:synchronousErrorEndpoint"/>
            <setBody>
                <constant><![CDATA[<root/>]]></constant>
            </setBody>

            <choice>
                <when>
                    <simple>${headers.operationName}</simple>
                    <doTry>
                        <toD uri="xslt:xslt/operation/${header.operationName}.xsl"/>
                        <doCatch>
                            <exception>java.lang.Exception</exception>
                            <log message="${exception}" loggingLevel="TRACE"/>
                            <to uri="xslt:xslt/DefaultResponse.xsl"/>
                        </doCatch>
                    </doTry>
                </when>
                <otherwise>
                    <to uri="xslt:xslt/DefaultResponse.xsl"/>
                </otherwise>
            </choice>
        </route>
        
        <route id="synchronous-json-error-handler-route">
            <from uri="direct:synchronousJSONErrorEndpoint"/>
            <choice>
                <when>
                    <simple>${headers.operationName}</simple>
                    <doTry>
                        <toD uri="freemarker:ftl/${header.operationName}.ftl?interpolationSyntax=22&amp;tagSyntax=2"/>
                        
                        <doCatch>
                            <exception>java.lang.Exception</exception>
                            <log message="${exception}" loggingLevel="TRACE"/>
                            <to uri="freemarker:ftl/DefaultResponse.ftl?contentCache=true&amp;interpolationSyntax=22&amp;tagSyntax=2"/>
                        </doCatch>
                    </doTry>
                </when>
                <otherwise>
                    <to uri="freemarker:ftl/DefaultResponse.ftl?allowContextMapAll=true&amp;contentCache=true"/>
                </otherwise>
            </choice>
        </route>
		
		<route id="soap-wrap-route">
			<from uri="direct-vm:soap-wrap"/>
			<log loggingLevel="TRACE" message="wrap: ${body.class.name}\n${body}"/>
			<doTry>
				<convertBodyTo type="org.w3c.dom.Document"/>
				<doCatch>
					<exception>java.lang.Exception</exception>
					<!--keep silence-->
				</doCatch>
			</doTry>
			<setProperty name="message">
				<simple>${body}</simple>
			</setProperty>
			<setBody>
				<simple>${null}</simple>
			</setBody>
			<to uri="xslt:xslt/soapWrap.xsl?failOnNullBody=false"/>
		</route>
		
    </camelContext>

</blueprint>
