public final void processResponseInternal(BaseMessageContextImpl ctx) {
    MessageContextAccessorImpl.blockPreviousContext();
    try {
      ServiceDesc serviceDesc = ctx.getServiceDesc();
      try {
        Dispatcher requestDispatcher = serviceDesc.getRequestDispatcher();
        requestDispatcher.retrieve(ctx, ctx.getFutureResponse());

      } catch (Throwable e) {
        handleRequestDispatchException(ctx, e);
        handleAbortedRequestDispatch(ctx);
      }

      runResponseSequence(ctx);
      Long processingStartTime =
          (Long) ctx.getProperty(SystemMetricDefs.CTX_KEY_MSG_PROCESSING_STARTED);
      updateMonitoringAfterProcessing(ctx, processingStartTime.longValue());
    } finally {
      MessageContextAccessorImpl.resetContext();
    }
  }
  void runResponseSequence(BaseMessageContextImpl ctx) {
    ServiceDesc serviceDesc = ctx.getServiceDesc();

    long startTime = System.nanoTime();

    // flip the processing stage
    ctx.changeProcessingStage(MessageProcessingStage.RESPONSE_PIPELINE);

    ctx.runLoggingHandlerStage(LoggingHandlerStage.RESPONSE_STARTED);

    // run the protocol processor
    try {
      ProtocolProcessor protocol = ctx.getProtocolProcessor();
      protocol.beforeResponsePipeline(ctx);
    } catch (Throwable e) {
      handleResponseProtocolProcessingException(ctx, e);
    }

    // run the pipeline
    try {
      Pipeline responsePipeline = serviceDesc.getResponsePipeline();
      responsePipeline.invoke(ctx);
    } catch (Throwable e) {
      handleResponseProcessingException(ctx, e);
    }

    // run the protocol processor
    try {
      ProtocolProcessor protocol = ctx.getProtocolProcessor();
      protocol.beforeResponseDispatch(ctx);
    } catch (Throwable e) {
      handleResponseProtocolProcessingException(ctx, e);
    }

    // flip the processing stage
    ctx.changeProcessingStage(MessageProcessingStage.RESPONSE_DISPATCH);

    boolean canProcessResponse = true;
    try {
      processPreResponseDispatchErrors(ctx);
    } catch (Throwable e) {
      // cannot do anything if errors are not processed
      canProcessResponse = false;
      handlePostResponseDispatchException(ctx, e);
    }

    long duration = System.nanoTime() - startTime;
    ctx.updateSvcAndOpMetric(SystemMetricDefs.OP_TIME_PIPELINE_RESPONSE, startTime, duration);

    if (canProcessResponse) {
      /*
       * Returning response is a seperate step. It is also abstracted out
       * through a dispatcher. In the case of a servlet, the
       * responseDispatcher is simply writing the data to the
       * HTTPResponse's outputstream. In the case of a Loaclbinding and
       * client cases, the response dispatcher simply returns and does
       * nothing. In the case of an asynchronous model, the response
       * dispatcher may use a connection and send the response back.
       */

      // TODO: add timing for response dispatcher on server (both sync and
      // async cases)
      // TO DO: fix async case - we should use callback and uplink here as
      // we cannot call logger yet
      try {
        // run through the response dispatcher.
        Dispatcher responseDispatcher = serviceDesc.getResponseDispatcher();
        responseDispatcher.dispatchSynchronously(ctx);
      } catch (Throwable e) {
        handlePostResponseDispatchException(ctx, e);
      }
    }

    ctx.changeProcessingStage(MessageProcessingStage.RESPONSE_COMPLETE);

    ctx.runLoggingHandlerStage(LoggingHandlerStage.RESPONSE_COMPLETE);
  }