com.helger.phase4.peppol.Phase4PeppolSender Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of phase4-peppol-client Show documentation
Show all versions of phase4-peppol-client Show documentation
Peppol AS4 client for outgoing transmissions
/**
* Copyright (C) 2015-2020 Philip Helger (www.helger.com)
* philip[at]helger[dot]com
*
* Licensed 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 com.helger.phase4.peppol;
import java.security.cert.X509Certificate;
import java.time.LocalDateTime;
import java.util.Locale;
import java.util.UUID;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.OverridingMethodsMustInvokeSuper;
import javax.annotation.WillNotClose;
import javax.annotation.concurrent.Immutable;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ResponseHandler;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.unece.cefact.namespaces.sbdh.StandardBusinessDocument;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.helger.bdve.executorset.VESID;
import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.Nonempty;
import com.helger.commons.collection.impl.CommonsArrayList;
import com.helger.commons.collection.impl.ICommonsList;
import com.helger.commons.datetime.PDTFactory;
import com.helger.commons.http.HttpHeaderMap;
import com.helger.commons.io.stream.NonBlockingByteArrayInputStream;
import com.helger.commons.mime.CMimeType;
import com.helger.commons.mime.IMimeType;
import com.helger.commons.state.ESuccess;
import com.helger.commons.state.ETriState;
import com.helger.commons.string.StringHelper;
import com.helger.commons.traits.IGenericImplTrait;
import com.helger.commons.wrapper.Wrapper;
import com.helger.httpclient.HttpClientFactory;
import com.helger.httpclient.response.ResponseHandlerHttpEntity;
import com.helger.peppol.sbdh.CPeppolSBDH;
import com.helger.peppol.sbdh.PeppolSBDHDocument;
import com.helger.peppol.sbdh.write.PeppolSBDHDocumentWriter;
import com.helger.peppol.smpclient.ISMPServiceMetadataProvider;
import com.helger.peppol.url.IPeppolURLProvider;
import com.helger.peppol.url.PeppolURLProvider;
import com.helger.peppol.utils.EPeppolCertificateCheckResult;
import com.helger.peppol.utils.PeppolCertificateChecker;
import com.helger.peppol.utils.PeppolCertificateHelper;
import com.helger.peppolid.IDocumentTypeIdentifier;
import com.helger.peppolid.IParticipantIdentifier;
import com.helger.peppolid.IProcessIdentifier;
import com.helger.peppolid.factory.PeppolIdentifierFactory;
import com.helger.phase4.CAS4;
import com.helger.phase4.attachment.EAS4CompressionMode;
import com.helger.phase4.attachment.IIncomingAttachmentFactory;
import com.helger.phase4.attachment.WSS4JAttachment;
import com.helger.phase4.client.AS4ClientSentMessage;
import com.helger.phase4.client.AS4ClientUserMessage;
import com.helger.phase4.client.IAS4ClientBuildMessageCallback;
import com.helger.phase4.crypto.AS4CryptoFactory;
import com.helger.phase4.crypto.IAS4CryptoFactory;
import com.helger.phase4.dump.IAS4IncomingDumper;
import com.helger.phase4.dump.IAS4OutgoingDumper;
import com.helger.phase4.ebms3header.Ebms3Error;
import com.helger.phase4.ebms3header.Ebms3Property;
import com.helger.phase4.ebms3header.Ebms3SignalMessage;
import com.helger.phase4.messaging.domain.MessageHelperMethods;
import com.helger.phase4.model.pmode.IPMode;
import com.helger.phase4.model.pmode.resolve.DefaultPModeResolver;
import com.helger.phase4.model.pmode.resolve.IPModeResolver;
import com.helger.phase4.profile.peppol.PeppolPMode;
import com.helger.phase4.servlet.AS4IncomingHandler;
import com.helger.phase4.servlet.AS4IncomingHandler.IAS4ParsedMessageCallback;
import com.helger.phase4.servlet.IAS4MessageState;
import com.helger.phase4.servlet.soap.SOAPHeaderElementProcessorRegistry;
import com.helger.phase4.soap.ESOAPVersion;
import com.helger.phase4.util.AS4ResourceHelper;
import com.helger.sbdh.builder.SBDHWriter;
import com.helger.xml.serialize.read.DOMReader;
/**
* This class contains all the specifics to send AS4 messages to PEPPOL. See
* sendAS4Message
as the main method to trigger the sending, with
* all potential customization.
*
* @author Philip Helger
*/
@Immutable
public final class Phase4PeppolSender
{
public static final PeppolIdentifierFactory IF = PeppolIdentifierFactory.INSTANCE;
public static final IPeppolURLProvider URL_PROVIDER = PeppolURLProvider.INSTANCE;
public static final String DEFAULT_SBDH_DOCUMENT_IDENTIFICATION_UBL_VERSION_ID = CPeppolSBDH.TYPE_VERSION_21;
private static final Logger LOGGER = LoggerFactory.getLogger (Phase4PeppolSender.class);
private Phase4PeppolSender ()
{}
@Nullable
public static Ebms3SignalMessage parseSignalMessage (@Nonnull final IAS4CryptoFactory aCryptoFactory,
@Nonnull final IPModeResolver aPModeResolver,
@Nonnull final IIncomingAttachmentFactory aIAF,
@Nonnull @WillNotClose final AS4ResourceHelper aResHelper,
@Nullable final IPMode aSendingPMode,
@Nonnull final Locale aLocale,
@Nonnull final HttpResponse aHttpResponse,
@Nonnull final byte [] aResponsePayload,
@Nullable final IAS4IncomingDumper aIncomingDumper) throws Phase4PeppolException
{
final Wrapper aRetWrapper = new Wrapper <> ();
// Handler for the parsed message
final IAS4ParsedMessageCallback aCallback = (aHttpHeaders, aSoapDocument, eSoapVersion, aIncomingAttachments) -> {
final ICommonsList aErrorMessages = new CommonsArrayList <> ();
// Use the sending PMode as fallback, because from the incoming
// receipt/error it is impossible to detect a PMode
final SOAPHeaderElementProcessorRegistry aRegistry = SOAPHeaderElementProcessorRegistry.createDefault (aPModeResolver,
aCryptoFactory,
aSendingPMode);
// Parse AS4, verify signature etc
final IAS4MessageState aState = AS4IncomingHandler.processEbmsMessage (aResHelper,
aLocale,
aRegistry,
aHttpHeaders,
aSoapDocument,
eSoapVersion,
aIncomingAttachments,
aErrorMessages);
// Remember the parsed signal message
aRetWrapper.set (aState.getEbmsSignalMessage ());
};
// Create header map from response headers
final HttpHeaderMap aHttpHeaders = new HttpHeaderMap ();
for (final Header aHeader : aHttpResponse.getAllHeaders ())
aHttpHeaders.addHeader (aHeader.getName (), aHeader.getValue ());
// Parse incoming message
try (final NonBlockingByteArrayInputStream aPayloadIS = new NonBlockingByteArrayInputStream (aResponsePayload))
{
AS4IncomingHandler.parseAS4Message (aIAF, aResHelper, aPayloadIS, aHttpHeaders, aCallback, aIncomingDumper);
}
catch (final Exception ex)
{
throw new Phase4PeppolException ("Error parsing signal message", ex);
}
// This one contains the result
return aRetWrapper.get ();
}
private static void _sendHttp (@Nonnull final IAS4CryptoFactory aCryptoFactory,
@Nonnull final IPModeResolver aPModeResolver,
@Nonnull final IIncomingAttachmentFactory aIAF,
@Nonnull final AS4ClientUserMessage aClientUserMsg,
@Nonnull final Locale aLocale,
@Nonnull final String sURL,
@Nullable final IAS4ClientBuildMessageCallback aBuildMessageCallback,
@Nullable final IAS4OutgoingDumper aOutgoingDumper,
@Nullable final IAS4IncomingDumper aIncomingDumper,
@Nullable final IPhase4PeppolResponseConsumer aResponseConsumer,
@Nullable final IPhase4PeppolSignalMessageConsumer aSignalMsgConsumer) throws Exception
{
if (LOGGER.isInfoEnabled ())
LOGGER.info ("Sending AS4 message to '" + sURL + "' with max. " + aClientUserMsg.getMaxRetries () + " retries");
if (LOGGER.isDebugEnabled ())
{
LOGGER.debug (" ServiceType = '" + aClientUserMsg.getServiceType () + "'");
LOGGER.debug (" Service = '" + aClientUserMsg.getServiceValue () + "'");
LOGGER.debug (" Action = '" + aClientUserMsg.getAction () + "'");
LOGGER.debug (" ConversationId = '" + aClientUserMsg.getConversationID () + "'");
LOGGER.debug (" MessageProperties:");
for (final Ebms3Property p : aClientUserMsg.ebms3Properties ())
LOGGER.debug (" [" + p.getName () + "] = [" + p.getValue () + "]");
LOGGER.debug (" Attachments (" + aClientUserMsg.attachments ().size () + "):");
for (final WSS4JAttachment a : aClientUserMsg.attachments ())
{
LOGGER.debug (" [" +
a.getId () +
"] with [" +
a.getMimeType () +
"] and [" +
a.getCharsetOrDefault (null) +
"] and [" +
a.getCompressionMode () +
"] and [" +
a.getContentTransferEncoding () +
"]");
}
}
final Wrapper aWrappedResponse = new Wrapper <> ();
final ResponseHandler aResponseHdl = aHttpResponse -> {
final HttpEntity aEntity = ResponseHandlerHttpEntity.INSTANCE.handleResponse (aHttpResponse);
if (aEntity == null)
return null;
aWrappedResponse.set (aHttpResponse);
return EntityUtils.toByteArray (aEntity);
};
final AS4ClientSentMessage aResponseEntity = aClientUserMsg.sendMessageWithRetries (sURL,
aResponseHdl,
aBuildMessageCallback,
aOutgoingDumper);
if (LOGGER.isInfoEnabled ())
LOGGER.info ("Successfully transmitted AS4 document with message ID '" +
aResponseEntity.getMessageID () +
"' to '" +
sURL +
"'");
if (aResponseConsumer != null)
aResponseConsumer.handleResponse (aResponseEntity);
// Try interpret result as SignalMessage
if (aResponseEntity.hasResponse () && aResponseEntity.getResponse ().length > 0)
{
// Read response as EBMS3 Signal Message
final Ebms3SignalMessage aSignalMessage = parseSignalMessage (aCryptoFactory,
aPModeResolver,
aIAF,
aClientUserMsg.getAS4ResourceHelper (),
aClientUserMsg.getPMode (),
aLocale,
aWrappedResponse.get (),
aResponseEntity.getResponse (),
aIncomingDumper);
if (aSignalMessage != null && aSignalMsgConsumer != null)
aSignalMsgConsumer.handleSignalMessage (aSignalMessage);
}
else
LOGGER.info ("AS4 ResponseEntity is empty");
}
@Nonnull
public static StandardBusinessDocument createSBDH (@Nonnull final IParticipantIdentifier aSenderID,
@Nonnull final IParticipantIdentifier aReceiverID,
@Nonnull final IDocumentTypeIdentifier aDocTypeID,
@Nonnull final IProcessIdentifier aProcID,
@Nullable final String sInstanceIdentifier,
@Nullable final String sUBLVersion,
@Nonnull final Element aPayloadElement)
{
final PeppolSBDHDocument aData = new PeppolSBDHDocument (IF);
aData.setSender (aSenderID.getScheme (), aSenderID.getValue ());
aData.setReceiver (aReceiverID.getScheme (), aReceiverID.getValue ());
aData.setDocumentType (aDocTypeID.getScheme (), aDocTypeID.getValue ());
aData.setProcess (aProcID.getScheme (), aProcID.getValue ());
aData.setDocumentIdentification (aPayloadElement.getNamespaceURI (),
StringHelper.hasText (sUBLVersion) ? sUBLVersion
: DEFAULT_SBDH_DOCUMENT_IDENTIFICATION_UBL_VERSION_ID,
aPayloadElement.getLocalName (),
StringHelper.hasText (sInstanceIdentifier) ? sInstanceIdentifier
: UUID.randomUUID ().toString (),
PDTFactory.getCurrentLocalDateTime ());
aData.setBusinessMessage (aPayloadElement);
final StandardBusinessDocument aSBD = new PeppolSBDHDocumentWriter ().createStandardBusinessDocument (aData);
return aSBD;
}
/**
* @param aPayloadElement
* The payload element to be validated. May not be null
.
* @param aVESID
* The VESID to validate against. May be null
.
* @param aValidationResultHandler
* The validation result handler to be used. May be null
.
* @throws Phase4PeppolException
* If the validation result handler decides to do so....
*/
private static void _validatePayload (@Nonnull final Element aPayloadElement,
@Nullable final VESID aVESID,
@Nullable final IPhase4PeppolValidatonResultHandler aValidationResultHandler) throws Phase4PeppolException
{
// Client side validation
if (aVESID != null)
{
if (aValidationResultHandler != null)
Phase4PeppolValidation.validateOutgoingBusinessDocument (aPayloadElement, aVESID, aValidationResultHandler);
else
LOGGER.warn ("A VES ID is present but no ValidationResultHandler - therefore no validation is performed");
}
else
if (aValidationResultHandler != null)
LOGGER.warn ("A ValidationResultHandler is present but no VESID - therefore no validation is performed");
}
/**
* @param aSenderID
* Sender participant ID. May not be null
.
* @param aReceiverID
* Receiver participant ID. May not be null
.
* @param aDocTypeID
* Document type ID. May not be null
.
* @param aProcID
* Process ID. May not be null
.
* @param sSBDHInstanceIdentifier
* SBDH instance identifier. May be null
to create a
* random ID.
* @param sSBDHUBLVersionID
* SBDH UBL version ID. May be null
to use the default.
* @param aPayloadElement
* Payload element to be wrapped. May not be null
.
* @return The byte array of the XML representation of the created SBDH. Never
* null
.
*/
@Nonnull
private static byte [] _createSBDH (@Nonnull final IParticipantIdentifier aSenderID,
@Nonnull final IParticipantIdentifier aReceiverID,
@Nonnull final IDocumentTypeIdentifier aDocTypeID,
@Nonnull final IProcessIdentifier aProcID,
@Nullable final String sSBDHInstanceIdentifier,
@Nullable final String sSBDHUBLVersionID,
@Nonnull final Element aPayloadElement)
{
ValueEnforcer.notNull (aPayloadElement, "PayloadElement");
ValueEnforcer.notNull (aPayloadElement.getNamespaceURI (), "PayloadElement.NamespaceURI");
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Start creating SBDH for AS4 message");
final StandardBusinessDocument aSBD = createSBDH (aSenderID,
aReceiverID,
aDocTypeID,
aProcID,
sSBDHInstanceIdentifier,
sSBDHUBLVersionID,
aPayloadElement);
final byte [] aSBDBytes = SBDHWriter.standardBusinessDocument ().getAsBytes (aSBD);
return aSBDBytes;
}
/**
* Get the receiver certificate from the specified SMP endpoint.
*
* @param aReceiverCert
* The determined receiver AP certificate to check. Never
* null
.
* @param aCertificateConsumer
* An optional consumer that is invoked with the received AP
* certificate to be used for the transmission. The certification check
* result must be considered when used. May be null
.
* @throws Phase4PeppolException
* in case of error
*/
private static void _checkReceiverAPCert (@Nullable final X509Certificate aReceiverCert,
@Nullable final IPhase4PeppolCertificateCheckResultHandler aCertificateConsumer) throws Phase4PeppolException
{
if (LOGGER.isDebugEnabled ())
LOGGER.debug ("Using the following receiver AP certificate from the SMP: " + aReceiverCert);
final LocalDateTime aNow = PDTFactory.getCurrentLocalDateTime ();
final EPeppolCertificateCheckResult eCertCheckResult = PeppolCertificateChecker.checkPeppolAPCertificate (aReceiverCert,
aNow,
ETriState.UNDEFINED,
ETriState.UNDEFINED);
// Interested in the certificate?
if (aCertificateConsumer != null)
aCertificateConsumer.onCertificateCheckResult (aReceiverCert, aNow, eCertCheckResult);
if (eCertCheckResult.isInvalid ())
{
throw new Phase4PeppolException ("The configured receiver AP certificate is not valid (at " +
aNow +
") and cannot be used for sending. Aborting. Reason: " +
eCertCheckResult.getReason ());
}
}
/**
* Send an AS4 message. It is highly recommend to use the {@link Builder}
* class, because it is very likely, that this API is NOT stable.
*
* @param aHttpClientFactory
* The HTTP client factory to be used. May not be null
.
* @param aCryptoFactory
* The crypto factory to be used. May not be null
.
* @param aPModeResolver
* PMode resolver. May not be null
.
* @param aSrcPMode
* The source PMode to be used. May not be null
.
* @param aSenderID
* The Peppol sending participant ID to be used. May not be
* null
.
* @param aReceiverID
* The Peppol receiving participant ID to send to. May not be
* null
.
* @param aDocTypeID
* The Peppol Document type ID to be used. May not be null
* @param aProcessID
* The Peppol process ID to be used. May not be null
.
* @param sSenderPartyID
* The sending party ID (the CN part of the senders certificate
* subject). May not be null
.
* @param sConversationID
* The AS4 conversation to be used. If none is provided, a random UUID
* is used. May be null
.
* @param aEndpoint
* The determined SMP endpoint. Never null
.
* @param aCertificateConsumer
* An optional consumer that is invoked with the received AP
* certificate to be used for the transmission. The certification check
* result must be considered when used. May be null
.
* @param aPayloadSBDBytes
* The Peppol SBDH payload to be send. May not be null
.
* @param aPayloadMimeType
* The MIME type of the payload. Usually "application/xml". May not be
* null
.
* @param bCompressPayload
* true
to use AS4 compression on the payload,
* false
to not compress it.
* @param aBuildMessageCallback
* An internal callback to do something with the build Ebms message.
* May be null
.
* @param aOutgoingDumper
* An outgoing dumper to be used. Maybe null
. If
* null
the global outgoing dumper is used.
* @param aIncomingDumper
* An incoming dumper to be used. Maybe null
. If
* null
the global incoming dumper is used.
* @param aResponseConsumer
* An optional consumer for the AS4 message that was sent. May be
* null
.
* @param aSignalMsgConsumer
* An optional consumer that will contain the parsed Ebms3 response
* signal message. May be null
.
* @throws Phase4PeppolException
* if something goes wrong
*/
private static void _sendAS4Message (@Nonnull final HttpClientFactory aHttpClientFactory,
@Nonnull final IAS4CryptoFactory aCryptoFactory,
@Nonnull final IPModeResolver aPModeResolver,
@Nonnull final IIncomingAttachmentFactory aIAF,
@Nonnull final IPMode aSrcPMode,
@Nonnull final IParticipantIdentifier aSenderID,
@Nonnull final IParticipantIdentifier aReceiverID,
@Nonnull final IDocumentTypeIdentifier aDocTypeID,
@Nonnull final IProcessIdentifier aProcessID,
@Nonnull @Nonempty final String sSenderPartyID,
@Nullable final String sConversationID,
@Nonnull final X509Certificate aReceiverCert,
@Nonnull @Nonempty final String sReceiverEndpointURL,
@Nonnull final byte [] aPayloadSBDBytes,
@Nonnull final IMimeType aPayloadMimeType,
final boolean bCompressPayload,
@Nullable final IAS4ClientBuildMessageCallback aBuildMessageCallback,
@Nullable final IAS4OutgoingDumper aOutgoingDumper,
@Nullable final IAS4IncomingDumper aIncomingDumper,
@Nullable final IPhase4PeppolResponseConsumer aResponseConsumer,
@Nullable final IPhase4PeppolSignalMessageConsumer aSignalMsgConsumer) throws Phase4PeppolException
{
ValueEnforcer.notNull (aHttpClientFactory, "HttpClientFactory");
ValueEnforcer.notNull (aCryptoFactory, "CryptoFactory");
ValueEnforcer.notNull (aSrcPMode, "SrcPMode");
ValueEnforcer.notNull (aSenderID, "SenderID");
ValueEnforcer.notNull (aReceiverID, "ReceiverID");
ValueEnforcer.notNull (aDocTypeID, "DocTypeID");
ValueEnforcer.notNull (aProcessID, "ProcID");
ValueEnforcer.notEmpty (sSenderPartyID, "SenderPartyID");
ValueEnforcer.notNull (aReceiverCert, "ReceiverCert");
ValueEnforcer.notEmpty (sReceiverEndpointURL, "ReceiverEndpointURL");
ValueEnforcer.notNull (aPayloadSBDBytes, "PayloadSBDBytes");
ValueEnforcer.notNull (aPayloadMimeType, "PayloadMimeType");
// Temporary file manager
try (final AS4ResourceHelper aResHelper = new AS4ResourceHelper ())
{
// Start building AS4 User Message
final AS4ClientUserMessage aUserMsg = new AS4ClientUserMessage (aResHelper);
aUserMsg.setHttpClientFactory (aHttpClientFactory);
// Otherwise Oxalis dies
aUserMsg.setQuoteHttpHeaders (false);
aUserMsg.setSOAPVersion (ESOAPVersion.SOAP_12);
// Set the keystore/truststore parameters
aUserMsg.setAS4CryptoFactory (aCryptoFactory);
aUserMsg.setPMode (aSrcPMode, true);
// Set after PMode
aUserMsg.cryptParams ().setCertificate (aReceiverCert);
// Explicit parameters have precedence over PMode
aUserMsg.setAgreementRefValue (PeppolPMode.DEFAULT_AGREEMENT_ID);
// The eb3:AgreementRef element also includes an optional attribute pmode
// which can be used to include the PMode.ID. This attribute MUST NOT be
// used as Access Points may use just one generic P-Mode for receiving
// messages.
aUserMsg.setPModeIDFactory (x -> null);
aUserMsg.setServiceType (aProcessID.getScheme ());
aUserMsg.setServiceValue (aProcessID.getValue ());
aUserMsg.setAction (aDocTypeID.getURIEncoded ());
aUserMsg.setConversationID (StringHelper.hasText (sConversationID) ? sConversationID
: UUID.randomUUID ().toString ());
// Backend or gateway?
aUserMsg.setFromPartyIDType (PeppolPMode.DEFAULT_PARTY_TYPE_ID);
aUserMsg.setFromPartyID (sSenderPartyID);
aUserMsg.setToPartyIDType (PeppolPMode.DEFAULT_PARTY_TYPE_ID);
aUserMsg.setToPartyID (PeppolCertificateHelper.getSubjectCN (aReceiverCert));
aUserMsg.ebms3Properties ()
.add (MessageHelperMethods.createEbms3Property (CAS4.ORIGINAL_SENDER, aSenderID.getURIEncoded ()));
aUserMsg.ebms3Properties ()
.add (MessageHelperMethods.createEbms3Property (CAS4.FINAL_RECIPIENT, aReceiverID.getURIEncoded ()));
// No payload - only one attachment
aUserMsg.setPayload (null);
// Add SBDH as attachment only
aUserMsg.addAttachment (WSS4JAttachment.createOutgoingFileAttachment (aPayloadSBDBytes,
null,
"document.xml",
aPayloadMimeType,
bCompressPayload ? EAS4CompressionMode.GZIP
: null,
aResHelper));
// Main sending
_sendHttp (aCryptoFactory,
aPModeResolver,
aIAF,
aUserMsg,
Locale.US,
sReceiverEndpointURL,
aBuildMessageCallback,
aOutgoingDumper,
aIncomingDumper,
aResponseConsumer,
aSignalMsgConsumer);
}
catch (final Phase4PeppolException ex)
{
// Re-throw
throw ex;
}
catch (final Exception ex)
{
// wrap
throw new Phase4PeppolException ("Wrapped Phase4PeppolException", ex);
}
}
/**
* @return Create a new Builder for AS4 messages if the payload is present and
* the SBDH should be created internally. Never null
.
* @since 0.9.4
*/
@Nonnull
public static Builder builder ()
{
return new Builder ();
}
/**
* @return Create a new Builder for AS4 messages if the SBDH payload is
* already present. Never null
.
* @since 0.9.6
*/
@Nonnull
public static SBDHBuilder sbdhBuilder ()
{
return new SBDHBuilder ();
}
/**
* Abstract builder base class with the minimum requirements configuration
*
* @author Philip Helger
* @param
* The implementation type
* @since 0.9.6
*/
public static abstract class AbstractBaseBuilder > implements
IGenericImplTrait
{
protected HttpClientFactory m_aHttpClientFactory;
protected IAS4CryptoFactory m_aCryptoFactory;
protected IPModeResolver m_aPModeResolver;
protected IIncomingAttachmentFactory m_aIAF;
protected IPMode m_aPMode;
protected IParticipantIdentifier m_aSenderID;
protected IParticipantIdentifier m_aReceiverID;
protected IDocumentTypeIdentifier m_aDocTypeID;
protected IProcessIdentifier m_aProcessID;
protected String m_sSenderPartyID;
protected String m_sConversationID;
protected IMimeType m_aPayloadMimeType;
protected boolean m_bCompressPayload;
protected IPhase4PeppolEndpointDetailProvider m_aEndpointDetailProvider;
protected IAS4ClientBuildMessageCallback m_aBuildMessageCallback;
protected IAS4OutgoingDumper m_aOutgoingDumper;
protected IAS4IncomingDumper m_aIncomingDumper;
protected IPhase4PeppolResponseConsumer m_aResponseConsumer;
protected IPhase4PeppolSignalMessageConsumer m_aSignalMsgConsumer;
/**
* Create a new builder, with the following fields already set:
* {@link #setHttpClientFactory(HttpClientFactory)}
* {@link #setCryptoFactory(IAS4CryptoFactory)}
* {@link #setPModeResolver(IPModeResolver)}
* {@link #setIncomingAttachmentFactory(IIncomingAttachmentFactory)}
* {@link #setPMode(IPMode)}
* {@link #setPayloadMimeType(IMimeType)}
* {@link #setCompressPayload(boolean)}
*/
protected AbstractBaseBuilder ()
{
// Set default values
try
{
setHttpClientFactory (new Phase4PeppolHttpClientFactory ());
setCryptoFactory (AS4CryptoFactory.getDefaultInstance ());
final IPModeResolver aPModeResolver = DefaultPModeResolver.DEFAULT_PMODE_RESOLVER;
setPModeResolver (aPModeResolver);
setIncomingAttachmentFactory (IIncomingAttachmentFactory.DEFAULT_INSTANCE);
setPMode (aPModeResolver.getPModeOfID (null, "s", "a", "i", "r", null));
setPayloadMimeType (CMimeType.APPLICATION_XML);
setCompressPayload (true);
}
catch (final Exception ex)
{
throw new IllegalStateException ("Failed to init AS4 Client builder", ex);
}
}
/**
* Set the HTTP client factory to be used. By default an instance of
* {@link Phase4PeppolHttpClientFactory} is used and there is no need to
* invoke this method.
*
* @param aHttpClientFactory
* The new HTTP client factory to be used. May not be
* null
.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setHttpClientFactory (@Nonnull final HttpClientFactory aHttpClientFactory)
{
ValueEnforcer.notNull (aHttpClientFactory, "HttpClientFactory");
m_aHttpClientFactory = aHttpClientFactory;
return thisAsT ();
}
/**
* Set the crypto factory to be used. The default crypto factory uses the
* properties from the file "crypto.properties".
*
* @param aCryptoFactory
* The crypto factory to be used. May not be null
.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setCryptoFactory (@Nonnull final IAS4CryptoFactory aCryptoFactory)
{
ValueEnforcer.notNull (aCryptoFactory, "CryptoFactory");
m_aCryptoFactory = aCryptoFactory;
return thisAsT ();
}
/**
* Set the PMode resolver to be used.
*
* @param aPModeResolver
* The PMode resolver to be used. May not be null
.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setPModeResolver (@Nonnull final IPModeResolver aPModeResolver)
{
ValueEnforcer.notNull (aPModeResolver, "aPModeResolver");
m_aPModeResolver = aPModeResolver;
return thisAsT ();
}
/**
* Set the incoming attachment factory to be used.
*
* @param aIAF
* The incoming attachment factory to be used. May not be
* null
.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setIncomingAttachmentFactory (@Nonnull final IIncomingAttachmentFactory aIAF)
{
ValueEnforcer.notNull (aIAF, "IAF");
m_aIAF = aIAF;
return thisAsT ();
}
/**
* @return The used P-Mode. Never null
.
* @since 0.9.7
*/
@Nonnull
public final IPMode getPMode ()
{
return m_aPMode;
}
/**
* Set the PMode to be used. By default a generic PMode for Peppol purposes
* is used so there is no need to invoke this method.
*
* @param aPMode
* The PMode to be used. May not be null
.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setPMode (@Nonnull final IPMode aPMode)
{
ValueEnforcer.notNull (aPMode, "PMode");
m_aPMode = aPMode;
return thisAsT ();
}
/**
* Set the sender participant ID of the message. The participant ID must be
* provided prior to sending.
*
* @param aSenderID
* The sender participant ID. May not be null
.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setSenderParticipantID (@Nonnull final IParticipantIdentifier aSenderID)
{
ValueEnforcer.notNull (aSenderID, "SenderID");
m_aSenderID = aSenderID;
return thisAsT ();
}
/**
* Set the receiver participant ID of the message. The participant ID must
* be provided prior to sending.
*
* @param aReceiverID
* The receiver participant ID. May not be null
.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setReceiverParticipantID (@Nonnull final IParticipantIdentifier aReceiverID)
{
ValueEnforcer.notNull (aReceiverID, "ReceiverID");
m_aReceiverID = aReceiverID;
return thisAsT ();
}
/**
* Set the document type ID to be send. The document type must be provided
* prior to sending.
*
* @param aDocTypeID
* The document type ID to be used. May not be null
.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setDocumentTypeID (@Nonnull final IDocumentTypeIdentifier aDocTypeID)
{
ValueEnforcer.notNull (aDocTypeID, "DocTypeID");
m_aDocTypeID = aDocTypeID;
return thisAsT ();
}
/**
* Set the process ID to be send. The process ID must be provided prior to
* sending.
*
* @param aProcessID
* The process ID to be used. May not be null
.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setProcessID (@Nonnull final IProcessIdentifier aProcessID)
{
ValueEnforcer.notNull (aProcessID, "ProcessID");
m_aProcessID = aProcessID;
return thisAsT ();
}
/**
* Set the "sender party ID" which is the CN part of the PEPPOL AP
* certificate. An example value is e.g. "POP000123" but it MUST match the
* certificate you are using. This must be provided prior to sending.
*
* @param sSenderPartyID
* The sender party ID. May neither be null
nor empty.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setSenderPartyID (@Nonnull @Nonempty final String sSenderPartyID)
{
ValueEnforcer.notEmpty (sSenderPartyID, "SenderPartyID");
m_sSenderPartyID = sSenderPartyID;
return thisAsT ();
}
/**
* Set the optional AS4 conversation ID. If this field is not set, a random
* conversation ID is created.
*
* @param sConversationID
* The optional AS4 conversation ID to be used. May be
* null
.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setConversationID (@Nullable final String sConversationID)
{
m_sConversationID = sConversationID;
return thisAsT ();
}
/**
* Set the abstract endpoint detail provider to be used. This can be an SMP
* lookup routine or in certain test cases a predefined certificate and
* endpoint URL.
*
* @param aEndpointDetailProvider
* The endpoint detail provider to be used. May not be
* null
.
* @return this for chaining
* @see #setSMPClient(ISMPServiceMetadataProvider)
*/
@Nonnull
public final IMPLTYPE setEndpointDetailProvider (@Nonnull final IPhase4PeppolEndpointDetailProvider aEndpointDetailProvider)
{
ValueEnforcer.notNull (aEndpointDetailProvider, "EndpointDetailProvider");
m_aEndpointDetailProvider = aEndpointDetailProvider;
return thisAsT ();
}
/**
* Set the SMP client to be used. This is the point where e.g. the
* differentiation between SMK and SML can be done. This must be set prior
* to sending.
*
* @param aSMPClient
* The SMP client to be used. May not be null
.
* @return this for chaining
* @see #setEndpointDetailProvider(IPhase4PeppolEndpointDetailProvider)
*/
@Nonnull
public final IMPLTYPE setSMPClient (@Nonnull final ISMPServiceMetadataProvider aSMPClient)
{
return setEndpointDetailProvider (new Phase4PeppolEndpointDetailProviderSMP (aSMPClient));
}
@Nonnull
public final IMPLTYPE setReceiverEndpointDetails (@Nonnull final X509Certificate aCert,
@Nonnull @Nonempty final String sDestURL)
{
return setEndpointDetailProvider (new Phase4PeppolEndpointDetailProviderConstant (aCert, sDestURL));
}
/**
* Set the MIME type of the payload. By default it is
* application/xml
and MUST usually not be changed. This value
* is required for sending.
*
* @param aPayloadMimeType
* The payload MIME type. May not be null
.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setPayloadMimeType (@Nonnull final IMimeType aPayloadMimeType)
{
ValueEnforcer.notNull (aPayloadMimeType, "PayloadMimeType");
m_aPayloadMimeType = aPayloadMimeType;
return thisAsT ();
}
/**
* Enable or disable the AS4 compression of the payload. By default
* compression is disabled.
*
* @param bCompressPayload
* true
to compress the payload, false
to
* not compress it.
* @return this for chaining.
*/
@Nonnull
public final IMPLTYPE setCompressPayload (final boolean bCompressPayload)
{
m_bCompressPayload = bCompressPayload;
return thisAsT ();
}
/**
* Set a internal message callback. Usually this method is NOT needed. Use
* only when you know what you are doing.
*
* @param aBuildMessageCallback
* An internal to be used for the created message. Maybe
* null
.
* @return this for chaining
* @since 0.9.6
*/
@Nonnull
public final IMPLTYPE setBuildMessageCallback (@Nullable final IAS4ClientBuildMessageCallback aBuildMessageCallback)
{
m_aBuildMessageCallback = aBuildMessageCallback;
return thisAsT ();
}
/**
* Set a specific outgoing dumper for this builder.
*
* @param aOutgoingDumper
* An outgoing dumper to be used. Maybe null
. If
* null
the global outgoing dumper is used.
* @return this for chaining
* @since 0.9.6
*/
@Nonnull
public final IMPLTYPE setOutgoingDumper (@Nullable final IAS4OutgoingDumper aOutgoingDumper)
{
m_aOutgoingDumper = aOutgoingDumper;
return thisAsT ();
}
/**
* Set a specific incoming dumper for this builder.
*
* @param aIncomingDumper
* An incoming dumper to be used. Maybe null
. If
* null
the global incoming dumper is used.
* @return this for chaining
* @since 0.9.7
*/
@Nonnull
public final IMPLTYPE setIncomingDumper (@Nullable final IAS4IncomingDumper aIncomingDumper)
{
m_aIncomingDumper = aIncomingDumper;
return thisAsT ();
}
/**
* Set an optional handler for the synchronous result message received from
* the other side. This method is optional and must not be called prior to
* sending.
*
* @param aResponseConsumer
* The optional response consumer. May be null
.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setResponseConsumer (@Nullable final IPhase4PeppolResponseConsumer aResponseConsumer)
{
m_aResponseConsumer = aResponseConsumer;
return thisAsT ();
}
/**
* Set an optional Ebms3 Signal Message Consumer. If this consumer is set,
* the response is trying to be parsed as a Signal Message. This method is
* optional and must not be called prior to sending.
*
* @param aSignalMsgConsumer
* The optional signal message consumer. May be null
.
* @return this for chaining
*/
@Nonnull
public final IMPLTYPE setSignalMsgConsumer (@Nullable final IPhase4PeppolSignalMessageConsumer aSignalMsgConsumer)
{
m_aSignalMsgConsumer = aSignalMsgConsumer;
return thisAsT ();
}
@OverridingMethodsMustInvokeSuper
public boolean isEveryRequiredFieldSet ()
{
if (m_aHttpClientFactory == null)
return false;
// m_aCryptoFactory may be null
if (m_aPMode == null)
return false;
if (m_aSenderID == null)
return false;
if (m_aReceiverID == null)
return false;
if (m_aDocTypeID == null)
return false;
if (m_aProcessID == null)
return false;
if (StringHelper.hasNoText (m_sSenderPartyID))
return false;
// m_sConversationID is optional
if (m_aEndpointDetailProvider == null)
return false;
if (m_aPayloadMimeType == null)
return false;
// m_bCompressPayload cannot be null
// m_aBuildMessageCallback may be null
// m_aOutgoingDumper may be null
// m_aIncomingDumper may be null
// m_aResponseConsumer may be null
// m_aSignalMsgConsumer may be null
return true;
}
/**
* Synchronously send the AS4 message. Before sending,
* {@link #isEveryRequiredFieldSet()} is called to check that the mandatory
* elements are set.
*
* @return {@link ESuccess#FAILURE} if not all mandatory parameters are set
* or if sending failed, {@link ESuccess#SUCCESS} upon success.
* Never null
.
* @throws Phase4PeppolException
* In case of any error
* @see #isEveryRequiredFieldSet()
*/
@Nonnull
public abstract ESuccess sendMessage () throws Phase4PeppolException;
}
/**
* The builder class for sending AS4 messages using Peppol specifics. Use
* {@link #sendMessage()} to trigger the main transmission.
* This builder class assumes, that only the payload (e.g. the Invoice) is
* present, and that both validation and SBDH creation happens inside.
*
* @author Philip Helger
* @since 0.9.4
*/
public static class Builder extends AbstractBaseBuilder
{
private String m_sSBDHInstanceIdentifier;
private String m_sSBDHUBLVersion;
private VESID m_aVESID;
private IPhase4PeppolValidatonResultHandler m_aValidationResultHandler;
private Element m_aPayloadElement;
private byte [] m_aPayloadBytes;
private IPhase4PeppolCertificateCheckResultHandler m_aCertificateConsumer;
/**
* Create a new builder, with the following fields already set:
* {@link #setHttpClientFactory(HttpClientFactory)}
* {@link #setPMode(IPMode)}
* {@link #setPayloadMimeType(IMimeType)}
* {@link #setCompressPayload(boolean)}
*/
public Builder ()
{}
/**
* Set the SBDH instance identifier. If none is provided, a random ID is
* used. Usually this must NOT be set.
*
* @param sSBDHInstanceIdentifier
* The SBDH instance identifier to be used. May be null
.
* @return this for chaining
*/
@Nonnull
public Builder setSBDHInstanceIdentifier (@Nullable final String sSBDHInstanceIdentifier)
{
m_sSBDHInstanceIdentifier = sSBDHInstanceIdentifier;
return this;
}
/**
* Set the SBDH document identification UBL version. If none is provided,
* the constant "2.1" is used.
*
* @param sSBDHUBLVersion
* The SBDH document identification UBL version to be used. May be
* null
.
* @return this for chaining
*/
@Nonnull
public Builder setSBDHUBLVersion (@Nullable final String sSBDHUBLVersion)
{
m_sSBDHUBLVersion = sSBDHUBLVersion;
return this;
}
/**
* Set the payload element to be used, if it is available as a parsed DOM
* element. If this method is called, it overwrites any other explicitly set
* payload.
*
* @param aPayloadElement
* The payload element to be used. They payload element MUST have a
* namespace URI. May not be null
.
* @return this for chaining
*/
@Nonnull
public Builder setPayload (@Nonnull final Element aPayloadElement)
{
ValueEnforcer.notNull (aPayloadElement, "Payload");
ValueEnforcer.notNull (aPayloadElement.getNamespaceURI (), "Payload.NamespaceURI");
m_aPayloadElement = aPayloadElement;
m_aPayloadBytes = null;
return this;
}
/**
* Set the payload to be used as a byte array. It will be parsed internally
* to a DOM element. If this method is called, it overwrites any other
* explicitly set payload.
*
* @param aPayloadBytes
* The payload bytes to be used. May not be null
.
* @return this for chaining
* @since 0.9.6
*/
@Nonnull
public Builder setPayload (@Nonnull final byte [] aPayloadBytes)
{
ValueEnforcer.notNull (aPayloadBytes, "PayloadBytes");
m_aPayloadBytes = aPayloadBytes;
m_aPayloadElement = null;
return this;
}
/**
* Set an optional Consumer for the retrieved certificate, independent of
* its usability.
*
* @param aCertificateConsumer
* The consumer to be used. The first parameter is the certificate
* itself and the second parameter is the internal check result. May
* be null
.
* @return this for chaining
*/
@Nonnull
public Builder setCertificateConsumer (@Nullable final IPhase4PeppolCertificateCheckResultHandler aCertificateConsumer)
{
m_aCertificateConsumer = aCertificateConsumer;
return this;
}
/**
* Set the client side validation to be used. If this method is not invoked,
* than it's the responsibility of the caller to validate the document prior
* to sending it. This method uses a default "do nothing validation result
* handler".
*
* @param aVESID
* The Validation Execution Set ID as in
* PeppolValidation390.VID_OPENPEPPOL_INVOICE_V3
. May be
* null
.
* @return this for chaining
* @see #setValidationConfiguration(VESID,
* IPhase4PeppolValidatonResultHandler)
*/
@Nonnull
public Builder setValidationConfiguration (@Nullable final VESID aVESID)
{
final IPhase4PeppolValidatonResultHandler aHdl = aVESID == null ? null
: new Phase4PeppolValidatonResultHandler ();
return setValidationConfiguration (aVESID, aHdl);
}
/**
* Set the client side validation to be used. If this method is not invoked,
* than it's the responsibility of the caller to validate the document prior
* to sending it. If the validation should happen internally, both the VESID
* AND the result handler must be set.
*
* @param aVESID
* The Validation Execution Set ID as in
* PeppolValidation390.VID_OPENPEPPOL_INVOICE_V3
. May be
* null
.
* @param aValidationResultHandler
* The validation result handler for positive and negative response
* handling. May be null
.
* @return this for chaining
*/
@Nonnull
public Builder setValidationConfiguration (@Nullable final VESID aVESID,
@Nullable final IPhase4PeppolValidatonResultHandler aValidationResultHandler)
{
m_aVESID = aVESID;
m_aValidationResultHandler = aValidationResultHandler;
return this;
}
@Override
public boolean isEveryRequiredFieldSet ()
{
if (!super.isEveryRequiredFieldSet ())
return false;
// m_sSBDHInstanceIdentifier may be null
// m_sSBDHUBLVersion may be null
if (m_aPayloadElement == null && m_aPayloadBytes == null)
return false;
// m_aCertificateConsumer may be null
// m_aVESID may be null
// m_aValidationResultHandler may be null
// All valid
return true;
}
@Override
@Nonnull
public ESuccess sendMessage () throws Phase4PeppolException
{
if (!isEveryRequiredFieldSet ())
{
LOGGER.error ("At least one mandatory field is not set and therefore the AS4 message cannot be send.");
return ESuccess.FAILURE;
}
// Ensure a DOM element is present
Element aPayloadElement = null;
if (m_aPayloadElement != null)
aPayloadElement = m_aPayloadElement;
else
if (m_aPayloadBytes != null)
{
// Parse it
final Document aDoc = DOMReader.readXMLDOM (m_aPayloadBytes);
if (aDoc == null)
throw new Phase4PeppolException ("Failed to parse payload bytes to a DOM node");
aPayloadElement = aDoc.getDocumentElement ();
if (aPayloadElement == null || aPayloadElement.getNamespaceURI () == null)
throw new Phase4PeppolException ("The parsed XML document must have a root element that has a namespace URI");
}
else
throw new IllegalStateException ("Unexpected - neither element nor bytes are present");
// Optional payload validation
_validatePayload (aPayloadElement, m_aVESID, m_aValidationResultHandler);
// e.g. SMP lookup
m_aEndpointDetailProvider.init (m_aDocTypeID, m_aProcessID, m_aReceiverID);
// Certificate from e.g. SMP lookup
final X509Certificate aReceiverCert = m_aEndpointDetailProvider.getReceiverAPCertificate ();
_checkReceiverAPCert (aReceiverCert, m_aCertificateConsumer);
// URL from e.g. SMP lookup
final String sDestURL = m_aEndpointDetailProvider.getReceiverAPEndpointURL ();
// Created SBDH
final byte [] aSBDBytes = _createSBDH (m_aSenderID,
m_aReceiverID,
m_aDocTypeID,
m_aProcessID,
m_sSBDHInstanceIdentifier,
m_sSBDHUBLVersion,
aPayloadElement);
_sendAS4Message (m_aHttpClientFactory,
m_aCryptoFactory,
m_aPModeResolver,
m_aIAF,
m_aPMode,
m_aSenderID,
m_aReceiverID,
m_aDocTypeID,
m_aProcessID,
m_sSenderPartyID,
m_sConversationID,
aReceiverCert,
sDestURL,
aSBDBytes,
m_aPayloadMimeType,
m_bCompressPayload,
m_aBuildMessageCallback,
m_aOutgoingDumper,
m_aIncomingDumper,
m_aResponseConsumer,
m_aSignalMsgConsumer);
return ESuccess.SUCCESS;
}
}
/**
* A builder class for sending AS4 messages using Peppol specifics. Use
* {@link #sendMessage()} to trigger the main transmission.
* This builder class assumes, that the SBDH was created outside, therefore no
* validation can occur.
*
* @author Philip Helger
* @since 0.9.6
*/
public static class SBDHBuilder extends AbstractBaseBuilder
{
private byte [] m_aPayloadBytes;
private IPhase4PeppolCertificateCheckResultHandler m_aCertificateConsumer;
/**
* Create a new builder, with the following fields already set:
* {@link #setHttpClientFactory(HttpClientFactory)}
* {@link #setPMode(IPMode)}
* {@link #setPayloadMimeType(IMimeType)}
* {@link #setCompressPayload(boolean)}
*/
public SBDHBuilder ()
{}
/**
* Set the payload to be used as a byte array.
*
* @param aSBDHBytes
* The SBDH bytes to be used. May not be null
.
* @return this for chaining
*/
@Nonnull
public SBDHBuilder setPayload (@Nonnull final byte [] aSBDHBytes)
{
ValueEnforcer.notNull (aSBDHBytes, "SBDHBytes");
m_aPayloadBytes = aSBDHBytes;
return this;
}
/**
* Set an optional Consumer for the retrieved certificate, independent of
* its usability.
*
* @param aCertificateConsumer
* The consumer to be used. The first parameter is the certificate
* itself and the second parameter is the internal check result. May
* be null
.
* @return this for chaining
*/
@Nonnull
public SBDHBuilder setCertificateConsumer (@Nullable final IPhase4PeppolCertificateCheckResultHandler aCertificateConsumer)
{
m_aCertificateConsumer = aCertificateConsumer;
return this;
}
@Override
public boolean isEveryRequiredFieldSet ()
{
if (!super.isEveryRequiredFieldSet ())
return false;
if (m_aPayloadBytes == null)
return false;
// m_aCertificateConsumer may be null
// All valid
return true;
}
@Override
@Nonnull
public ESuccess sendMessage () throws Phase4PeppolException
{
if (!isEveryRequiredFieldSet ())
{
LOGGER.error ("At least one mandatory field is not set and therefore the AS4 message cannot be send.");
return ESuccess.FAILURE;
}
// e.g. SMP lookup
m_aEndpointDetailProvider.init (m_aDocTypeID, m_aProcessID, m_aReceiverID);
// Certificate from e.g. SMP lookup
final X509Certificate aReceiverCert = m_aEndpointDetailProvider.getReceiverAPCertificate ();
_checkReceiverAPCert (aReceiverCert, m_aCertificateConsumer);
// URL from e.g. SMP lookup
final String sDestURL = m_aEndpointDetailProvider.getReceiverAPEndpointURL ();
_sendAS4Message (m_aHttpClientFactory,
m_aCryptoFactory,
m_aPModeResolver,
m_aIAF,
m_aPMode,
m_aSenderID,
m_aReceiverID,
m_aDocTypeID,
m_aProcessID,
m_sSenderPartyID,
m_sConversationID,
aReceiverCert,
sDestURL,
m_aPayloadBytes,
m_aPayloadMimeType,
m_bCompressPayload,
m_aBuildMessageCallback,
m_aOutgoingDumper,
m_aIncomingDumper,
m_aResponseConsumer,
m_aSignalMsgConsumer);
return ESuccess.SUCCESS;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy