/**
   * It is possible for us (Synapse) to cause the creation of a duplicate relatesTo as we try to
   * hold onto the outgoing message ID even for POX messages using the relates to Now once we get a
   * response, make sure we remove any trace of this before we proceed any further
   *
   * @param mc the message context from which a possibly duplicated relatesTo should be removed
   * @param relates the existing relatedTo array of the message
   */
  private void removeDuplicateRelatesTo(MessageContext mc, RelatesTo[] relates) {

    int insertPos = 0;
    RelatesTo[] newRelates = new RelatesTo[relates.length];

    for (RelatesTo current : relates) {
      boolean found = false;
      for (int j = 0; j < newRelates.length && j < insertPos; j++) {
        if (newRelates[j].equals(current) || newRelates[j].getValue().equals(current.getValue())) {
          found = true;
          break;
        }
      }
      if (!found) {
        newRelates[insertPos++] = current;
      }
    }

    RelatesTo[] trimmedRelates = new RelatesTo[insertPos];
    System.arraycopy(newRelates, 0, trimmedRelates, 0, insertPos);
    mc.setRelationships(trimmedRelates);
  }
  /**
   * Everytime a response message is received this method gets invoked. It will then select the
   * outgoing *Synapse* message context for the reply we received, and determine what action to take
   * at the Synapse level
   *
   * @param messageCtx the Axis2 message context of the reply received
   * @throws AxisFault
   */
  public void receive(MessageContext messageCtx) throws AxisFault {

    String messageID = null;

    /**
     * In an Out-only scenario if the client receives a HTTP 202 accepted we need to remove the call
     * back/s registered for that request. This if will check weather this is a message sent in a
     * that scenario and remove the callback
     */
    if (messageCtx.getProperty(NhttpConstants.HTTP_202_RECEIVED) != null
        && "true".equals(messageCtx.getProperty(NhttpConstants.HTTP_202_RECEIVED))) {
      if (callbackStore.containsKey(messageCtx.getMessageID())) {
        callbackStore.remove(messageCtx.getMessageID());
        if (log.isDebugEnabled()) {
          log.debug(
              "CallBack registered with Message id : "
                  + messageCtx.getMessageID()
                  + " removed from the "
                  + "callback store since we got an accepted Notification");
        }
      }

      return;
    }

    if (messageCtx.getOptions() != null && messageCtx.getOptions().getRelatesTo() != null) {
      // never take a chance with a NPE at this stage.. so check at each level :-)
      Options options = messageCtx.getOptions();
      if (options != null) {
        RelatesTo relatesTo = options.getRelatesTo();
        if (relatesTo != null) {
          messageID = relatesTo.getValue();
        }
      }
    } else if (messageCtx.getProperty(SandeshaClientConstants.SEQUENCE_KEY) == null) {
      messageID = (String) messageCtx.getProperty(SynapseConstants.RELATES_TO_FOR_POX);
    }

    if (messageID != null) {
      AsyncCallback callback = (AsyncCallback) callbackStore.remove(messageID);
      if (log.isDebugEnabled()) {
        log.debug(
            "Callback removed for request message id : "
                + messageID
                + ". Pending callbacks count : "
                + callbackStore.size());
      }

      RelatesTo[] relates = messageCtx.getRelationships();
      if (relates != null && relates.length > 1) {
        // we set a relates to to the response message so that if WSA is not used, we
        // could still link back to the original message. But if WSA was used, this
        // gets duplicated, and we should remove it
        removeDuplicateRelatesTo(messageCtx, relates);
      }

      if (callback != null) {
        handleMessage(
            messageID,
            messageCtx,
            ((AsyncCallback) callback).getSynapseOutMsgCtx(),
            (AsyncCallback) callback);

      } else {
        // TODO invoke a generic synapse error handler for this message
        log.warn(
            "Synapse received a response for the request with message Id : "
                + messageID
                + " But a callback is not registered (anymore) to process this response");
      }

    } else if (!messageCtx.isPropertyTrue(NhttpConstants.SC_ACCEPTED)) {
      // TODO invoke a generic synapse error handler for this message
      log.warn("Synapse received a response message without a message Id");
    }
  }
    private void send(MessageContext msgctx) throws AxisFault {

      // create the responseMessageContext and set that its related to the current outgoing
      // message, so that it could be tied back to the original request even if the response
      // envelope does not contain addressing headers
      MessageContext responseMessageContext = new MessageContext();
      responseMessageContext.setMessageID(msgctx.getMessageID());
      responseMessageContext.setProperty(
          SynapseConstants.RELATES_TO_FOR_POX, msgctx.getMessageID());
      responseMessageContext.setOptions(options);
      responseMessageContext.setServerSide(true);
      addMessageContext(responseMessageContext);
      responseMessageContext.setProperty("synapse.send", "true");

      AxisEngine.send(msgctx);

      // did the engine receive a immediate synchronous response?
      // e.g. sometimes the transport sender may listen for a syncronous reply
      if (msgctx.getProperty(MessageContext.TRANSPORT_IN) != null) {

        responseMessageContext.setOperationContext(msgctx.getOperationContext());
        responseMessageContext.setAxisMessage(
            msgctx
                .getOperationContext()
                .getAxisOperation()
                .getMessage(WSDLConstants.MESSAGE_LABEL_IN_VALUE));
        responseMessageContext.setAxisService(msgctx.getAxisService());

        responseMessageContext.setProperty(
            MessageContext.TRANSPORT_OUT, msgctx.getProperty(MessageContext.TRANSPORT_OUT));
        responseMessageContext.setProperty(
            org.apache.axis2.Constants.OUT_TRANSPORT_INFO,
            msgctx.getProperty(org.apache.axis2.Constants.OUT_TRANSPORT_INFO));

        responseMessageContext.setProperty(
            org.apache.synapse.SynapseConstants.ISRESPONSE_PROPERTY, Boolean.TRUE);
        responseMessageContext.setTransportIn(msgctx.getTransportIn());
        responseMessageContext.setTransportOut(msgctx.getTransportOut());

        // If request is REST assume that the responseMessageContext is REST too
        responseMessageContext.setDoingREST(msgctx.isDoingREST());

        responseMessageContext.setProperty(
            MessageContext.TRANSPORT_IN, msgctx.getProperty(MessageContext.TRANSPORT_IN));
        responseMessageContext.setTransportIn(msgctx.getTransportIn());
        responseMessageContext.setTransportOut(msgctx.getTransportOut());

        // Options object reused above so soapAction needs to be removed so
        // that soapAction+wsa:Action on response don't conflict
        responseMessageContext.setSoapAction("");

        if (responseMessageContext.getEnvelope() == null) {
          // If request is REST we assume the responseMessageContext is
          // REST, so set the variable

          Options options = responseMessageContext.getOptions();
          if (options != null) {
            RelatesTo relatesTo = options.getRelatesTo();
            if (relatesTo != null) {
              relatesTo.setValue(msgctx.getMessageID());
            } else {
              options.addRelatesTo(new RelatesTo(msgctx.getMessageID()));
            }
          }

          SOAPEnvelope resenvelope = TransportUtils.createSOAPMessage(responseMessageContext);

          if (resenvelope != null) {
            responseMessageContext.setEnvelope(resenvelope);
            AxisEngine.receive(responseMessageContext);
            if (responseMessageContext.getReplyTo() != null) {
              sc.setTargetEPR(responseMessageContext.getReplyTo());
            }

            complete(msgctx);
          } else {
            throw new AxisFault(Messages.getMessage("blockingInvocationExpectsResponse"));
          }
        }
      }
    }