/**
   * Add MAPs which are specific to the requestor or responder role.
   *
   * @param maps the MAPs being assembled
   * @param message the current message
   * @param isRequestor true iff the current messaging role is that of requestor
   * @param isFault true if a fault is being mediated
   */
  private void addRoleSpecific(
      AddressingProperties maps, Message message, boolean isRequestor, boolean isFault) {
    if (isRequestor) {
      Exchange exchange = message.getExchange();

      // add request-specific MAPs
      boolean isOneway = exchange.isOneWay();
      boolean isOutbound = ContextUtils.isOutbound(message);

      // To
      if (maps.getTo() == null) {
        Conduit conduit = null;
        if (isOutbound) {
          conduit = ContextUtils.getConduit(null, message);
        }
        String s = (String) message.get(Message.ENDPOINT_ADDRESS);
        EndpointReferenceType reference =
            conduit != null ? conduit.getTarget() : ContextUtils.getNoneEndpointReference();
        if (conduit != null
            && !StringUtils.isEmpty(s)
            && !reference.getAddress().getValue().equals(s)) {
          EndpointReferenceType ref = new EndpointReferenceType();
          AttributedURIType tp = new AttributedURIType();
          tp.setValue(s);
          ref.setAddress(tp);
          ref.setMetadata(reference.getMetadata());
          ref.setReferenceParameters(reference.getReferenceParameters());
          ref.getOtherAttributes().putAll(reference.getOtherAttributes());
          reference = ref;
        }
        maps.setTo(reference);
      }

      // ReplyTo, set if null in MAPs or if set to a generic address
      // (anonymous or none) that may not be appropriate for the
      // current invocation
      EndpointReferenceType replyTo = maps.getReplyTo();
      if (ContextUtils.isGenericAddress(replyTo)) {
        replyTo = getReplyTo(message, replyTo);
        if (replyTo == null
            || (isOneway
                && (replyTo == null
                    || replyTo.getAddress() == null
                    || !Names.WSA_NONE_ADDRESS.equals(replyTo.getAddress().getValue())))) {
          AttributedURIType address =
              ContextUtils.getAttributedURI(
                  isOneway ? Names.WSA_NONE_ADDRESS : Names.WSA_ANONYMOUS_ADDRESS);
          replyTo = ContextUtils.WSA_OBJECT_FACTORY.createEndpointReferenceType();
          replyTo.setAddress(address);
        }
        maps.setReplyTo(replyTo);
      }

      // FaultTo
      if (maps.getFaultTo() == null) {
        maps.setFaultTo(maps.getReplyTo());
      } else if (maps.getFaultTo().getAddress() == null) {
        maps.setFaultTo(null);
      }
    } else {
      // add response-specific MAPs
      AddressingProperties inMAPs = getMAPs(message, false, false);
      maps.exposeAs(inMAPs.getNamespaceURI());
      // To taken from ReplyTo or FaultTo in incoming MAPs (depending
      // on the fault status of the response)
      if (isFault && inMAPs.getFaultTo() != null) {
        maps.setTo(inMAPs.getFaultTo());
      } else if (maps.getTo() == null && inMAPs.getReplyTo() != null) {
        maps.setTo(inMAPs.getReplyTo());
      }

      // RelatesTo taken from MessageID in incoming MAPs
      if (inMAPs.getMessageID() != null
          && !Boolean.TRUE.equals(message.get(Message.PARTIAL_RESPONSE_MESSAGE))) {
        String inMessageID = inMAPs.getMessageID().getValue();
        maps.setRelatesTo(ContextUtils.getRelatesTo(inMessageID));
      } else {
        maps.setRelatesTo(ContextUtils.getRelatesTo(Names.WSA_UNSPECIFIED_RELATIONSHIP));
      }

      // fallback fault action
      if (isFault && maps.getAction() == null) {
        maps.setAction(ContextUtils.getAttributedURI(Names.WSA_DEFAULT_FAULT_ACTION));
      }

      if (isFault && !ContextUtils.isGenericAddress(inMAPs.getFaultTo())) {

        Message m = message.getExchange().getInFaultMessage();
        if (m == null) {
          m = message;
        }
        InternalContextUtils.rebaseResponse(inMAPs.getFaultTo(), inMAPs, m);

        Destination destination =
            InternalContextUtils.createDecoupledDestination(m.getExchange(), inMAPs.getFaultTo());
        m.getExchange().setDestination(destination);
      }
    }
  }
  /**
   * Mediate message flow.
   *
   * @param message the current message
   * @param isFault true if a fault is being mediated
   * @return true if processing should continue on dispatch path
   */
  protected boolean mediate(Message message, boolean isFault) {
    boolean continueProcessing = true;
    if (ContextUtils.isOutbound(message)) {
      if (usingAddressing(message)) {
        // request/response MAPs must be aggregated
        aggregate(message, isFault);
      }
      AddressingProperties theMaps =
          ContextUtils.retrieveMAPs(message, false, ContextUtils.isOutbound(message));
      if (null != theMaps) {
        if (ContextUtils.isRequestor(message)) {
          assertAddressing(message, theMaps.getReplyTo(), theMaps.getFaultTo());
        } else {
          checkReplyTo(message, theMaps);
        }
      }
    } else if (!ContextUtils.isRequestor(message)) {
      // responder validates incoming MAPs
      AddressingProperties maps = getMAPs(message, false, false);
      // check responses
      if (maps != null) {
        checkAddressingResponses(maps.getReplyTo(), maps.getFaultTo());
        assertAddressing(message, maps.getReplyTo(), maps.getFaultTo());
      }
      boolean isOneway = message.getExchange().isOneWay();
      if (null == maps && !addressingRequired) {
        return false;
      }
      continueProcessing = validateIncomingMAPs(maps, message);
      if (maps != null) {
        AddressingProperties theMaps =
            ContextUtils.retrieveMAPs(message, false, ContextUtils.isOutbound(message));
        if (null != theMaps) {
          assertAddressing(message, theMaps.getReplyTo(), theMaps.getFaultTo());
        }

        if (isOneway || !ContextUtils.isGenericAddress(maps.getReplyTo())) {
          InternalContextUtils.rebaseResponse(maps.getReplyTo(), maps, message);
        }
        if (!isOneway) {
          if (ContextUtils.isNoneAddress(maps.getReplyTo())) {
            LOG.warning("Detected NONE value in ReplyTo WSA header for request-respone MEP");
          } else {
            // ensure the inbound MAPs are available in both the full & fault
            // response messages (used to determine relatesTo etc.)
            ContextUtils.propogateReceivedMAPs(maps, message.getExchange());
          }
        }
      }
      if (continueProcessing) {
        // any faults thrown from here on can be correlated with this message
        message.put(FaultMode.class, FaultMode.LOGICAL_RUNTIME_FAULT);
      } else {
        // validation failure => dispatch is aborted, response MAPs
        // must be aggregated
        // isFault = true;
        // aggregate(message, isFault);
        if (isSOAP12(message)) {
          SoapFault soap12Fault =
              new SoapFault(
                  ContextUtils.retrieveMAPFaultReason(message), Soap12.getInstance().getSender());
          soap12Fault.setSubCode(
              new QName(Names.WSA_NAMESPACE_NAME, ContextUtils.retrieveMAPFaultName(message)));
          throw soap12Fault;
        }
        throw new SoapFault(
            ContextUtils.retrieveMAPFaultReason(message),
            new QName(Names.WSA_NAMESPACE_NAME, ContextUtils.retrieveMAPFaultName(message)));
      }
    } else {
      AddressingProperties theMaps =
          ContextUtils.retrieveMAPs(message, false, ContextUtils.isOutbound(message));
      if (null != theMaps) {
        assertAddressing(message, theMaps.getReplyTo(), theMaps.getFaultTo());
      }
      // If the wsa policy is enabled , but the client sets the
      // WSAddressingFeature.isAddressingRequired to false , we need to assert all WSA assertion to
      // true
      if (!ContextUtils.isOutbound(message)
          && ContextUtils.isRequestor(message)
          && getWSAddressingFeature(message) != null
          && !getWSAddressingFeature(message).isAddressingRequired()) {
        assertAddressing(message);
      }
      // CXF-3060 :If wsa policy is not enforced, AddressingProperties map is null and
      // AddressingFeature.isRequired, requestor checks inbound message and throw exception
      if (null == theMaps
          && !ContextUtils.isOutbound(message)
          && ContextUtils.isRequestor(message)
          && getWSAddressingFeature(message) != null
          && getWSAddressingFeature(message).isAddressingRequired()) {
        boolean missingWsaHeader = false;
        AssertionInfoMap aim = message.get(AssertionInfoMap.class);
        if (aim == null || aim.size() == 0) {
          missingWsaHeader = true;
        }
        if (aim != null && aim.size() > 0) {
          missingWsaHeader = true;
          QName[] types =
              new QName[] {
                MetadataConstants.ADDRESSING_ASSERTION_QNAME,
                MetadataConstants.USING_ADDRESSING_2004_QNAME,
                MetadataConstants.USING_ADDRESSING_2005_QNAME,
                MetadataConstants.USING_ADDRESSING_2006_QNAME
              };
          for (QName type : types) {
            for (AssertionInfo assertInfo : aim.getAssertionInfo(type)) {
              if (assertInfo.isAsserted()) {
                missingWsaHeader = false;
              }
            }
          }
        }
        if (missingWsaHeader) {
          throw new SoapFault(
              "MISSING_ACTION_MESSAGE",
              BUNDLE,
              new QName(Names.WSA_NAMESPACE_NAME, Names.HEADER_REQUIRED_NAME));
        }
      }
      if (MessageUtils.isPartialResponse(message)
          && message.getExchange().getOutMessage() != null) {
        // marked as a partial response, let's see if it really is
        MessageInfo min = message.get(MessageInfo.class);
        MessageInfo mout = message.getExchange().getOutMessage().get(MessageInfo.class);
        if (min != null
            && mout != null
            && min.getOperation() == mout.getOperation()
            && message.getContent(List.class) != null) {
          // the in and out messages are on the same operation
          // and we were able to get a response for it.
          message.remove(Message.PARTIAL_RESPONSE_MESSAGE);
        }
      }
    }
    return continueProcessing;
  }