diff --git a/phase4-dbnalliance-client/pom.xml b/phase4-dbnalliance-client/pom.xml
index 6ea7e081a..c197030b8 100644
--- a/phase4-dbnalliance-client/pom.xml
+++ b/phase4-dbnalliance-client/pom.xml
@@ -37,8 +37,8 @@
http://www.apache.org/licenses/LICENSE-2.0
repo
-
-
+
+
com.helger.phase4
@@ -48,6 +48,23 @@
com.helger.phase4
phase4-dynamic-discovery
+
+ com.helger
+ ph-xhe
+ 4.0.1
+ jar
+
+
+ com.helger.peppol
+ peppol-commons
+ ${peppol-commons.version}
+
+
+ com.helger.peppol
+ dbnalliance-xhe
+ 9.4.1-SNAPSHOT
+ jar
+
com.helger.phase4
phase4-profile-dbnalliance
diff --git a/phase4-dbnalliance-client/src/main/java/com/helger/phase4/dbnalliance/Phase4DBNAllianceSender.java b/phase4-dbnalliance-client/src/main/java/com/helger/phase4/dbnalliance/Phase4DBNAllianceSender.java
index a2928cbbb..61b8810e8 100644
--- a/phase4-dbnalliance-client/src/main/java/com/helger/phase4/dbnalliance/Phase4DBNAllianceSender.java
+++ b/phase4-dbnalliance-client/src/main/java/com/helger/phase4/dbnalliance/Phase4DBNAllianceSender.java
@@ -30,21 +30,35 @@
import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.Nonempty;
+import com.helger.commons.datetime.XMLOffsetDateTime;
import com.helger.commons.state.ESuccess;
+import com.helger.peppol.smp.ESMPTransportProfile;
import com.helger.peppol.utils.PeppolCertificateHelper;
+import com.helger.peppol.xhe.DBNAlliancePayload;
+import com.helger.peppol.xhe.DBNAllianceXHEData;
+import com.helger.peppol.xhe.write.DBNAllianceXHEDocumentWriter;
import com.helger.peppolid.IDocumentTypeIdentifier;
import com.helger.peppolid.IParticipantIdentifier;
import com.helger.peppolid.IProcessIdentifier;
+import com.helger.peppolid.factory.BDXR2IdentifierFactory;
import com.helger.phase4.CAS4;
+import com.helger.phase4.attachment.AS4OutgoingAttachment;
import com.helger.phase4.crypto.ICryptoSessionKeyProvider;
import com.helger.phase4.dynamicdiscovery.AS4EndpointDetailProviderBDXR2;
import com.helger.phase4.dynamicdiscovery.AS4EndpointDetailProviderConstant;
import com.helger.phase4.dynamicdiscovery.IAS4EndpointDetailProvider;
+import com.helger.phase4.mgr.MetaAS4Manager;
import com.helger.phase4.profile.dbnalliance.DBNAlliancePMode;
import com.helger.phase4.sender.AbstractAS4UserMessageBuilderMIMEPayload;
import com.helger.phase4.sender.IAS4SendingDateTimeConsumer;
import com.helger.phase4.util.Phase4Exception;
import com.helger.smpclient.bdxr2.IBDXR2ServiceMetadataProvider;
+import com.helger.xhe.v10.CXHE10;
+import com.helger.xhe.v10.XHE10Marshaller;
+import com.helger.xhe.v10.XHE10XHEType;
+import java.nio.charset.StandardCharsets;
+import java.util.UUID;
+import org.w3c.dom.Element;
/**
* This class contains all the specifics to send AS4 messages with the
@@ -56,10 +70,46 @@
@Immutable
public final class Phase4DBNAllianceSender
{
+ public static final BDXR2IdentifierFactory IF = BDXR2IdentifierFactory.INSTANCE;
private static final Logger LOGGER = LoggerFactory.getLogger (Phase4DBNAllianceSender.class);
private Phase4DBNAllianceSender ()
{}
+
+ @Nullable
+ private static XHE10XHEType _createXHE (@Nonnull final IParticipantIdentifier aSenderID,
+ @Nonnull final IParticipantIdentifier aReceiverID,
+ @Nonnull final IDocumentTypeIdentifier aDocTypeID,
+ @Nonnull final IProcessIdentifier aProcID,
+ @Nonnull final Element aPayloadElement,
+ final boolean bClonePayloadElement
+ )
+ {
+ final DBNAllianceXHEData aData = new DBNAllianceXHEData (IF);
+ aData.setFromParty (aSenderID.getScheme (), aSenderID.getValue ());
+ aData.setToParty (aReceiverID.getScheme (), aReceiverID.getValue ());
+ aData.setID (UUID.randomUUID ().toString ());
+ aData.setCreationDateAndTime (MetaAS4Manager.getTimestampMgr ().getCurrentXMLDateTime ());
+
+ final DBNAlliancePayload aPayload = new DBNAlliancePayload (IF);
+ aPayload.setCustomizationID (null, aDocTypeID.getValue ());
+ aPayload.setProfileID (aProcID.getScheme (), aProcID.getValue ());
+
+ // Not cloning the payload element is for saving memory only (if it can be
+ // ensured, the source payload element is not altered externally of course)
+ if (bClonePayloadElement)
+ aPayload.setPayloadContent (aPayloadElement);
+ else
+ aPayload.setPayloadContentNoClone (aPayloadElement);
+
+ aData.addPayload(aPayload);
+
+ // check with logging
+ if (!aData.areAllFieldsSet (true))
+ throw new IllegalArgumentException ("The DBNAlliance XHE data is incomplete. See logs for details.");
+
+ return DBNAllianceXHEDocumentWriter.createExchangeHeaderEnvelope (aData);
+ }
/**
* @return Create a new Builder for AS4 messages if the XHE payload is
@@ -83,6 +133,7 @@ public abstract static class AbstractDBNAllianceUserMessageBuilder
{
// C4
+ protected IParticipantIdentifier m_aSenderID;
protected IParticipantIdentifier m_aReceiverID;
protected IDocumentTypeIdentifier m_aDocTypeID;
protected IProcessIdentifier m_aProcessID;
@@ -114,6 +165,24 @@ protected AbstractDBNAllianceUserMessageBuilder ()
throw new IllegalStateException ("Failed to init AS4 Client builder", ex);
}
}
+
+ /**
+ * 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 senderParticipantID (@Nonnull final IParticipantIdentifier aSenderID)
+ {
+ ValueEnforcer.notNull (aSenderID, "SenderID");
+ if (m_aSenderID != null)
+ LOGGER.warn ("An existing SenderParticipantID is overridden");
+ m_aSenderID = aSenderID;
+ return thisAsT ();
+ }
/**
* Set the receiver participant ID of the message. The participant ID must
@@ -226,7 +295,9 @@ public final IMPLTYPE endpointDetailProvider (@Nonnull final IAS4EndpointDetailP
@Nonnull
public final IMPLTYPE smpClient (@Nonnull final IBDXR2ServiceMetadataProvider aSMPClient)
{
- return endpointDetailProvider (new AS4EndpointDetailProviderBDXR2 (aSMPClient));
+ final AS4EndpointDetailProviderBDXR2 aEndpointDetailProvider = new AS4EndpointDetailProviderBDXR2 (aSMPClient);
+ aEndpointDetailProvider.setTransportProfile (ESMPTransportProfile.TRANSPORT_PROFILE_DBNA_AS4_v1);
+ return endpointDetailProvider (aEndpointDetailProvider);
}
/**
@@ -420,7 +491,83 @@ protected void customizeBeforeSending () throws Phase4Exception
public static class DBNAllianceUserMessageBuilder extends
AbstractDBNAllianceUserMessageBuilder
{
+
+ private Element m_aPayloadElement;
+
public DBNAllianceUserMessageBuilder ()
{}
+
+ /**
+ * Set the payload element to be used, if it is available as a parsed DOM
+ * element. Internally the DOM element will be cloned before sending it out.
+ * 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 DBNAllianceUserMessageBuilder payload (@Nonnull final Element aPayloadElement)
+ {
+ ValueEnforcer.notNull (aPayloadElement, "Payload");
+ ValueEnforcer.notNull (aPayloadElement.getNamespaceURI (), "Payload.NamespaceURI");
+ m_aPayloadElement = aPayloadElement;
+ return this;
+ }
+
+ @Override
+ protected ESuccess finishFields () throws Phase4Exception
+ {
+ // Ensure a DOM element is present
+ final Element aPayloadElement;
+ final boolean bClonePayloadElement;
+ if (m_aPayloadElement != null)
+ {
+ // Already provided as a DOM element
+ aPayloadElement = m_aPayloadElement;
+ bClonePayloadElement = true;
+ }
+ else
+ throw new IllegalStateException ("Unexpected - element are not present");
+
+ // Consistency check
+ if (CXHE10.NAMESPACE_URI_XHE.equals(aPayloadElement.getNamespaceURI ()))
+ throw new Phase4DBNAllianceException ("You cannot set a Exchange Header Envelope as the payload for the regular builder. The XHE is created automatically inside of this builder.");
+
+ // Optional payload validation
+ // _validatePayload (aPayloadElement, m_aVESRegistry, m_aVESID, m_aValidationResultHandler);
+
+ // Perform SMP lookup
+ if (super.finishFields ().isFailure ())
+ return ESuccess.FAILURE;
+
+ // Created SBDH
+ if (LOGGER.isDebugEnabled ())
+ LOGGER.debug ("Start creating SBDH for AS4 message");
+
+ final XHE10XHEType aXHE = _createXHE (m_aSenderID,
+ m_aReceiverID,
+ m_aDocTypeID,
+ m_aProcessID,
+ aPayloadElement,
+ bClonePayloadElement);
+ if (aXHE == null)
+ {
+ // A log message was already provided
+ return ESuccess.FAILURE;
+ }
+
+ final byte [] aXHEBytes = new XHE10Marshaller ().getAsBytes (aXHE);
+
+ // Now we have the main payload
+ payload (AS4OutgoingAttachment.builder ()
+ .data (aXHEBytes)
+ .compressionGZIP ()
+ .mimeTypeXML ()
+ .charset (StandardCharsets.UTF_8));
+
+ return ESuccess.SUCCESS;
+ }
}
}
diff --git a/phase4-dbnalliance-client/src/test/java/com/helger/phase4/dbnalliance/MainPhase4DBNAllianceSenderExample.java b/phase4-dbnalliance-client/src/test/java/com/helger/phase4/dbnalliance/MainPhase4DBNAllianceSenderExample.java
index 449322f5e..81abc4941 100644
--- a/phase4-dbnalliance-client/src/test/java/com/helger/phase4/dbnalliance/MainPhase4DBNAllianceSenderExample.java
+++ b/phase4-dbnalliance-client/src/test/java/com/helger/phase4/dbnalliance/MainPhase4DBNAllianceSenderExample.java
@@ -16,20 +16,24 @@
*/
package com.helger.phase4.dbnalliance;
+import com.helger.peppolid.bdxr.smp2.participant.BDXR2ParticipantIdentifier;
+import com.helger.peppolid.factory.SimpleIdentifierFactory;
import java.io.File;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.helger.phase4.attachment.AS4OutgoingAttachment;
import com.helger.phase4.dump.AS4DumpManager;
import com.helger.phase4.dump.AS4IncomingDumperFileBased;
import com.helger.phase4.dump.AS4OutgoingDumperFileBased;
import com.helger.phase4.sender.AbstractAS4UserMessageBuilder.ESimpleUserMessageSendResult;
import com.helger.servlet.mock.MockServletContext;
+import com.helger.smpclient.bdxr2.BDXR2ClientReadOnly;
+import com.helger.smpclient.dbna.EDBNASML;
+import com.helger.smpclient.url.DBNAURLProviderSMP;
import com.helger.web.scope.mgr.WebScopeManager;
+import com.helger.xml.serialize.read.DOMReader;
+import org.w3c.dom.Element;
public class MainPhase4DBNAllianceSenderExample
{
@@ -47,23 +51,26 @@ public static void main (final String [] args)
try
{
// Read XML payload to send
- final byte [] aPayloadBytes = Files.readAllBytes (new File ("src/test/resources/external/examples/base-example.xml").toPath ());
- if (aPayloadBytes == null)
+ final Element aPayloadElement = DOMReader.readXMLDOM (new File ("src/test/resources/external/examples/base-example.xml"))
+ .getDocumentElement ();
+ if (aPayloadElement == null)
throw new IllegalStateException ("Failed to read file to be send");
- // Start configuring here
+ // Start configuring here
+ final BDXR2ParticipantIdentifier aReceiver = Phase4DBNAllianceSender.IF.createParticipantIdentifier ("us:ein", "365060483");
+ BDXR2ClientReadOnly aSMPClient = new BDXR2ClientReadOnly (DBNAURLProviderSMP.INSTANCE.getSMPURIOfParticipant (aReceiver,
+ EDBNASML.TEST.getZoneName()));
+ aSMPClient.setVerifySignature (false);
+
final ESimpleUserMessageSendResult eResult;
eResult = Phase4DBNAllianceSender.builder ()
- .fromPartyID ("AS4-Sender")
- .toPartyID ("AS4-Receiver")
- .endpointURL ("https://receiver.example.org/dbna/as4")
- .service ("AS4-Service")
- .action ("AS4-Action")
- .payload (AS4OutgoingAttachment.builder ()
- .data (aPayloadBytes)
- .compressionGZIP ()
- .mimeTypeXML ()
- .charset (StandardCharsets.UTF_8))
+ .documentTypeID (SimpleIdentifierFactory.INSTANCE.createDocumentTypeIdentifier ("bdx-docid-qns", "urn:oasis:names:specification:ubl:schema:xsd:Invoice-2::Invoice##DBNAlliance-1.0-data-Core"))
+ .processID (Phase4DBNAllianceSender.IF.createProcessIdentifier(null, "bdx:noprocess"))
+ .senderParticipantID (Phase4DBNAllianceSender.IF.createParticipantIdentifier ("us:ein", "365060483"))
+ .receiverParticipantID (aReceiver)
+ .fromPartyID("365060483")
+ .payload (aPayloadElement)
+ .smpClient (aSMPClient)
.sendMessageAndCheckForReceipt ();
LOGGER.info ("DBNAlliance send result: " + eResult);
}
diff --git a/phase4-dbnalliance-client/src/test/resources/application.properties b/phase4-dbnalliance-client/src/test/resources/application.properties
new file mode 100644
index 000000000..d012a0990
--- /dev/null
+++ b/phase4-dbnalliance-client/src/test/resources/application.properties
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2015-2024 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.
+#
+
+global.debug=true
+global.production=false
+global.nostartupinfo=true
+
+org.apache.wss4j.crypto.provider=org.apache.wss4j.common.crypto.Merlin
+org.apache.wss4j.crypto.merlin.keystore.type=pkcs12
+org.apache.wss4j.crypto.merlin.keystore.file=test-ap-2023.p12
+org.apache.wss4j.crypto.merlin.keystore.password=peppol
+org.apache.wss4j.crypto.merlin.keystore.alias=cert
+org.apache.wss4j.crypto.merlin.keystore.private.password=peppol
+
+org.apache.wss4j.crypto.merlin.load.cacerts=false
+#org.apache.wss4j.crypto.merlin.truststore.provider=
+org.apache.wss4j.crypto.merlin.truststore.type=jks
+org.apache.wss4j.crypto.merlin.truststore.file=truststore/dbnalliance-truststore.jks
+org.apache.wss4j.crypto.merlin.truststore.password=dbnalliance
+
+phase4.dump.path = generated/