/**
   * Creates 'wsu:Id' attribute for wsu:Timestamp needed for signature.
   *
   * @param message
   * @return
   * @throws ParserException
   */
  private String createTimestampUuid(SoapMessage message) throws ParserException {

    NodeList timestampList =
        message
            .getHeader()
            .getOwnerDocument()
            .getElementsByTagNameNS(WSU_NAMESPACE, WSU_TIMESTAMP_LOCAL_NAME);

    assert timestampList.getLength() <= 1;

    if (timestampList.getLength() == 1) {
      assert timestampList.item(0).getNodeType() == Node.ELEMENT_NODE;

      Element timestamp = (Element) timestampList.item(0);
      String timestampId = Util.randomNCNameUUID();

      Attr wsuId = timestamp.getOwnerDocument().createAttributeNS(WSU_NAMESPACE, WSU_ID_LOCAL_NAME);
      wsuId.setPrefix(timestamp.getPrefix());

      wsuId.setValue(timestampId);
      timestamp.setAttributeNodeNS(wsuId);
      timestamp.setIdAttributeNode(wsuId, true);

      log.trace("Created wsu:Id for wsu:Timestamp: " + timestampId);

      return timestampId;
    }

    log.trace("Timestamp element not found in the message");

    return null;
  }
  /**
   * Signs a SoapMessage with the holder-of-key configuration provided on class creation. This
   * method changes the SoapMessage.
   *
   * @param message cannot be null
   * @return The signed SoapMessage
   * @throws ParserException
   * @throws SignatureException
   */
  @Override
  public final SoapMessage sign(SoapMessage message) throws ParserException, SignatureException {

    assert message != null;

    Provider securityProvider = holderOfKeyConfig.getSecurityProvider();
    XMLSignatureFactory xmlSigFactory =
        (securityProvider != null)
            ? XMLSignatureFactory.getInstance("DOM", securityProvider)
            : XMLSignatureFactory.getInstance();

    try {
      String bodyUuid = createSoapBodyUuid(message);
      CanonicalizationMethod canonicalizationMethod =
          xmlSigFactory.newCanonicalizationMethod(
              CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null);
      SignatureMethod signatureMethod = getSignatureMethod(xmlSigFactory);
      ArrayList<String> refList = new ArrayList<String>();
      refList.add(bodyUuid);
      refList.add(createTimestampUuid(message));
      List<Reference> references = createSignatureReferences(xmlSigFactory, refList);
      SignedInfo signedInfo =
          xmlSigFactory.newSignedInfo(canonicalizationMethod, signatureMethod, references);

      KeyInfoFactory kif = KeyInfoFactory.getInstance();
      KeyInfo ki =
          kif.newKeyInfo(
              Collections.singletonList(new DOMStructure(createKeyInfoContent(message))));

      XMLSignature signature =
          xmlSigFactory.newXMLSignature(signedInfo, ki, null, addUseKeySignatureId(message), null);

      DOMSignContext dsc =
          new DOMSignContext(
              holderOfKeyConfig.getPrivateKey(), message.getHeader().getFirstChild());
      dsc.putNamespacePrefix(XMLSignature.XMLNS, DIGITAL_SIGNATURE_NAMESPACE_PREFIX);

      signature.sign(dsc);

      log.debug("Message with SOAPBody id: " + bodyUuid + " is signed.");
    } catch (NoSuchAlgorithmException e) {
      log.debug(CREATING_SIGNATURE_ERR_MSG);
      throw new SignatureException(CREATING_SIGNATURE_ERR_MSG, e);
    } catch (InvalidAlgorithmParameterException e) {
      log.debug(CREATING_SIGNATURE_ERR_MSG);
      throw new SignatureException(CREATING_SIGNATURE_ERR_MSG, e);
    } catch (MarshalException e) {
      log.debug(CREATING_SIGNATURE_ERR_MSG);
      throw new SignatureException(CREATING_SIGNATURE_ERR_MSG, e);
    } catch (XMLSignatureException e) {
      log.debug(CREATING_SIGNATURE_ERR_MSG);
      throw new SignatureException(CREATING_SIGNATURE_ERR_MSG, e);
    }

    return message;
  }
    @Override
    public void soap(SoapMessage message) throws Exception {
      log.trace("soap({})", message.getXml());

      requestSoap = (SoapMessageImpl) message;
      requestServiceId = requestSoap.getService();

      if (handler == null) {
        chooseHandler();
      }

      handler.soap(message);
    }
  /**
   * Creates 'wsu:Id' SOAP body attribute needed for signature.
   *
   * @param message
   * @return
   * @throws ParserException
   */
  private String createSoapBodyUuid(SoapMessage message) throws ParserException {

    String bodyId = Util.randomNCNameUUID();
    try {
      message
          .getMessage()
          .getSOAPBody()
          .addAttribute(new QName(WSU_NAMESPACE, WSU_ID_LOCAL_NAME, WSU_PREFIX), bodyId);
    } catch (SOAPException e) {
      log.debug(PARSING_XML_ERROR_MSG);
      throw new ParserException(PARSING_XML_ERROR_MSG, e);
    }

    log.debug("Created wsu:Id for SOAPBody: " + bodyId);

    return bodyId;
  }