# TargetErrorHandler.java

    /*
     *  Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
     *
     *  WSO2 Inc. licenses this file to you under the Apache License,
     *  Version 2.0 (the "License"); you may not use this file except
     *  in compliance with the License.
     *  You may obtain a copy of the License at
     *
     *  http://www.apache.org/licenses/LICENSE-2.0
     *
     *  Unless required by applicable law or agreed to in writing,
     *  software distributed under the License is distributed on an
     *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
     *  KIND, either express or implied.  See the License for the
     *  specific language governing permissions and limitations
     *  under the License.
     *
     */
    package org.apache.synapse.transport.netty.sender;

    import org.apache.axiom.soap.SOAPEnvelope;
    import org.apache.axis2.AxisFault;
    import org.apache.axis2.context.MessageContext;
    import org.apache.axis2.description.OutOnlyAxisOperation;
    import org.apache.axis2.engine.MessageReceiver;
    import org.apache.axis2.util.MessageContextBuilder;
    import org.apache.axis2.wsdl.WSDLConstants;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.apache.synapse.transport.netty.BridgeConstants;
    import org.apache.synapse.transport.netty.config.TargetConfiguration;

    import java.io.PrintWriter;
    import java.io.StringWriter;
    import java.io.Writer;

    /**
     * {@code TargetErrorHandler} will report back failure to the message receiver.
     */
    public class TargetErrorHandler {

        private static final Log LOG = LogFactory.getLog(TargetErrorHandler.class);

        private final TargetConfiguration targetConfiguration;

        public TargetErrorHandler(TargetConfiguration targetConfiguration) {

            this.targetConfiguration = targetConfiguration;
        }

        public void handleError(final MessageContext msgContext, final int errorCode, final String errorMessage,
                                final Throwable exceptionToRaise) {

            if (cannotProceedWithErrorHandling(msgContext, errorCode, errorMessage, exceptionToRaise)) {
                return;
            }

            targetConfiguration.getWorkerPool().execute(new Runnable() {
                public void run() {

                    MessageReceiver msgReceiver = msgContext.getAxisOperation().getMessageReceiver();

                    try {
                        AxisFault axisFault = (exceptionToRaise != null
                                ? new AxisFault(errorMessage, exceptionToRaise) : new AxisFault(errorMessage));

                        MessageContext faultMessageContext =
                                MessageContextBuilder.createFaultMessageContext(msgContext, axisFault);

                        if (LOG.isDebugEnabled()) {
                            LOG.debug("Sending Fault for Request with Message ID : " + msgContext.getMessageID());
                        }

                        populateProperties(faultMessageContext, msgContext, errorCode, errorMessage, exceptionToRaise);
                        msgReceiver.receive(faultMessageContext);

                    } catch (AxisFault af) {
                        LOG.error("Unable to report failure back to the message receiver", af);
                    }
                }
            });
        }

        /**
         * Populates required properties in the message context.
         */
        private void populateProperties(MessageContext faultMessageContext, MessageContext msgContext, int errorCode,
                                        String errorMessage, Throwable exceptionToRaise) {

            faultMessageContext.setTo(null);
            faultMessageContext.setServerSide(true);
            faultMessageContext.setDoingREST(msgContext.isDoingREST());
            faultMessageContext.setProperty(MessageContext.TRANSPORT_IN, msgContext
                    .getProperty(MessageContext.TRANSPORT_IN));
            faultMessageContext.setTransportIn(msgContext.getTransportIn());
            faultMessageContext.setTransportOut(msgContext.getTransportOut());
            faultMessageContext.setOperationContext(msgContext.getOperationContext());
            faultMessageContext.setConfigurationContext(msgContext.getConfigurationContext());

            if (!(msgContext.getOperationContext().getAxisOperation() instanceof OutOnlyAxisOperation)) {
                faultMessageContext.setAxisMessage(msgContext.getOperationContext().getAxisOperation()
                        .getMessage(WSDLConstants.MESSAGE_LABEL_IN_VALUE));
            }

            faultMessageContext.setProperty(BridgeConstants.SENDING_FAULT, Boolean.TRUE);
            faultMessageContext.setProperty(BridgeConstants.ERROR_MESSAGE, errorMessage);

            if (errorCode != -1) {
                faultMessageContext.setProperty(BridgeConstants.ERROR_CODE, getErrorCode(errorCode));
            }

            SOAPEnvelope envelope = faultMessageContext.getEnvelope();
            if (exceptionToRaise != null) {
                faultMessageContext.setProperty(BridgeConstants.ERROR_DETAIL, getStackTrace(exceptionToRaise));
                faultMessageContext.setProperty(BridgeConstants.ERROR_EXCEPTION, exceptionToRaise);
                envelope.getBody().getFault().getDetail().setText(exceptionToRaise.toString());
            } else {
                faultMessageContext.setProperty(BridgeConstants.ERROR_DETAIL, errorMessage);
                envelope.getBody().getFault().getDetail().setText(errorMessage);
            }

            faultMessageContext.setProperty(BridgeConstants.NO_ENTITY_BODY, true);
            faultMessageContext.setProperty(BridgeConstants.INTERNAL_EXCEPTION_ORIGIN,
                    msgContext.getProperty(BridgeConstants.INTERNAL_EXCEPTION_ORIGIN));
        }

        private boolean cannotProceedWithErrorHandling(MessageContext msgContext, int errorCode,
                                                       String errorMessage, Throwable exceptionToRaise) {

            if (errorCode == -1 && errorMessage == null && exceptionToRaise == null) {
                return true;
            }

            return msgContext.getAxisOperation() == null || msgContext.getAxisOperation().getMessageReceiver() == null;
        }

        private int getErrorCode(int errorCode) {

            return errorCode;
        }

        private String getStackTrace(Throwable aThrowable) {

            final Writer result = new StringWriter();
            final PrintWriter printWriter = new PrintWriter(result);
            aThrowable.printStackTrace(printWriter);
            return result.toString();
        }
    }