use of com.helger.phase4.util.Phase4Exception in project phase4 by phax.
the class AS4IncomingHandler method parseAS4Message.
public static void parseAS4Message(@Nonnull final IAS4IncomingAttachmentFactory aIAF, @Nonnull @WillNotClose final AS4ResourceHelper aResHelper, @Nonnull final IAS4IncomingMessageMetadata aMessageMetadata, @Nonnull @WillClose final InputStream aPayloadIS, @Nonnull final HttpHeaderMap aHttpHeaders, @Nonnull final IAS4ParsedMessageCallback aCallback, @Nullable final IAS4IncomingDumper aIncomingDumper) throws Phase4Exception, IOException, MessagingException, WSSecurityException {
// Determine content type
final String sContentType = aHttpHeaders.getFirstHeaderValue(CHttpHeader.CONTENT_TYPE);
if (StringHelper.hasNoText(sContentType))
throw new Phase4Exception("Content-Type header is missing");
if (LOGGER.isDebugEnabled())
LOGGER.debug("Received Content-Type string: '" + sContentType + "'");
final IMimeType aContentType = MimeTypeParser.safeParseMimeType(sContentType);
if (LOGGER.isDebugEnabled())
LOGGER.debug("Received Content-Type object: " + aContentType);
if (aContentType == null)
throw new Phase4Exception("Failed to parse Content-Type '" + sContentType + "'");
final IMimeType aPlainContentType = aContentType.getCopyWithoutParameters();
// Fallback to global dumper if none is provided
final IAS4IncomingDumper aRealIncomingDumper = aIncomingDumper != null ? aIncomingDumper : AS4DumpManager.getIncomingDumper();
Document aSoapDocument = null;
ESoapVersion eSoapVersion = null;
final ICommonsList<WSS4JAttachment> aIncomingAttachments = new CommonsArrayList<>();
final Wrapper<OutputStream> aDumpOSHolder = new Wrapper<>();
if (aPlainContentType.equals(AS4RequestHandler.MT_MULTIPART_RELATED)) {
// MIME message
if (LOGGER.isDebugEnabled())
LOGGER.debug("Received MIME message");
final String sBoundary = aContentType.getParameterValueWithName("boundary");
if (StringHelper.hasNoText(sBoundary))
throw new Phase4Exception("Content-Type '" + sContentType + "' misses 'boundary' parameter");
if (LOGGER.isDebugEnabled())
LOGGER.debug("MIME Boundary: '" + sBoundary + "'");
// Ensure the stream gets closed correctly
try (final InputStream aRequestIS = AS4DumpManager.getIncomingDumpAwareInputStream(aRealIncomingDumper, aPayloadIS, aMessageMetadata, aHttpHeaders, aDumpOSHolder)) {
// PARSING MIME Message via MultipartStream
final MultipartStream aMulti = new MultipartStream(aRequestIS, sBoundary.getBytes(StandardCharsets.ISO_8859_1), (MultipartProgressNotifier) null);
int nIndex = 0;
while (true) {
final boolean bHasNextPart = nIndex == 0 ? aMulti.skipPreamble() : aMulti.readBoundary();
if (!bHasNextPart)
break;
if (LOGGER.isDebugEnabled())
LOGGER.debug("Found MIME part #" + nIndex);
try (final MultipartItemInputStream aBodyPartIS = aMulti.createInputStream()) {
// Read headers AND content
final MimeBodyPart aBodyPart = new MimeBodyPart(aBodyPartIS);
if (nIndex == 0) {
// First MIME part -> SOAP document
if (LOGGER.isDebugEnabled())
LOGGER.debug("Parsing first MIME part as SOAP document");
// Read SOAP document
aSoapDocument = DOMReader.readXMLDOM(aBodyPart.getInputStream());
IMimeType aPlainPartMT = MimeTypeParser.safeParseMimeType(aBodyPart.getContentType());
if (aPlainPartMT != null)
aPlainPartMT = aPlainPartMT.getCopyWithoutParameters();
// Determine SOAP version from MIME part content type
eSoapVersion = ESoapVersion.getFromMimeTypeOrNull(aPlainPartMT);
if (eSoapVersion != null && LOGGER.isDebugEnabled())
LOGGER.debug("Determined SOAP version " + eSoapVersion + " from Content-Type");
if (eSoapVersion == null && aSoapDocument != null) {
// Determine SOAP version from the read document
eSoapVersion = ESoapVersion.getFromNamespaceURIOrNull(XMLHelper.getNamespaceURI(aSoapDocument));
if (eSoapVersion != null && LOGGER.isDebugEnabled())
LOGGER.debug("Determined SOAP version " + eSoapVersion + " from XML root element namespace URI");
}
} else {
// MIME Attachment (index is gt 0)
if (LOGGER.isDebugEnabled())
LOGGER.debug("Parsing MIME part #" + nIndex + " as attachment");
final WSS4JAttachment aAttachment = aIAF.createAttachment(aBodyPart, aResHelper);
aIncomingAttachments.add(aAttachment);
}
}
nIndex++;
}
}
if (LOGGER.isDebugEnabled())
LOGGER.debug("Read MIME message with " + aIncomingAttachments.size() + " attachment(s)");
} else {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Received plain message");
// Expect plain SOAP - read whole request to DOM
// Note: this may require a huge amount of memory for large requests
aSoapDocument = DOMReader.readXMLDOM(AS4DumpManager.getIncomingDumpAwareInputStream(aRealIncomingDumper, aPayloadIS, aMessageMetadata, aHttpHeaders, aDumpOSHolder));
if (LOGGER.isDebugEnabled())
if (aSoapDocument != null)
LOGGER.debug("Successfully parsed payload as XML");
else
LOGGER.debug("Failed to parse payload as XML");
if (aSoapDocument != null) {
// Determine SOAP version from the read document
eSoapVersion = ESoapVersion.getFromNamespaceURIOrNull(XMLHelper.getNamespaceURI(aSoapDocument));
if (eSoapVersion != null && LOGGER.isDebugEnabled())
LOGGER.debug("Determined SOAP version " + eSoapVersion + " from XML root element namespace URI");
}
if (eSoapVersion == null) {
// Determine SOAP version from content type
eSoapVersion = ESoapVersion.getFromMimeTypeOrNull(aPlainContentType);
if (eSoapVersion != null && LOGGER.isDebugEnabled())
LOGGER.debug("Determined SOAP version " + eSoapVersion + " from Content-Type");
}
}
try {
if (aSoapDocument == null) {
// We don't have a SOAP document
throw new Phase4Exception(eSoapVersion == null ? "Failed to parse incoming message!" : "Failed to parse incoming SOAP " + eSoapVersion.getVersion() + " document!");
}
if (eSoapVersion == null) {
// We're missing a SOAP version
throw new Phase4Exception("Failed to determine SOAP version of XML document!");
}
aCallback.handle(aHttpHeaders, aSoapDocument, eSoapVersion, aIncomingAttachments);
} finally {
// Here, the incoming dump is finally ready closed and usable
if (aRealIncomingDumper != null && aDumpOSHolder.isSet())
try {
aRealIncomingDumper.onEndRequest(aMessageMetadata);
} catch (final Exception ex) {
LOGGER.error("IncomingDumper.onEndRequest failed. Dumper=" + aRealIncomingDumper + "; MessageMetadata=" + aMessageMetadata, ex);
}
}
}
use of com.helger.phase4.util.Phase4Exception in project phase4 by phax.
the class AS4RequestHandler method handleRequest.
/**
* This is the main handling routine when called from an abstract
* (non-Servlet) API
*
* @param aServletRequestIS
* The input stream with the request data. May not be
* <code>null</code>.
* @param aRequestHttpHeaders
* The HTTP headers of the request. May not be <code>null</code>.
* @param aHttpResponse
* The HTTP response to be filled. May not be <code>null</code>.
* @throws Phase4Exception
* in case the request is missing certain prerequisites. Since 0.9.11
* @throws IOException
* In case of IO errors
* @throws MessagingException
* MIME related errors
* @throws WSSecurityException
* In case of WSS4J errors
* @see #handleRequest(InputStream, HttpHeaderMap, IAS4ResponseAbstraction)
* for a more generic API
*/
public void handleRequest(@Nonnull @WillClose final InputStream aServletRequestIS, @Nonnull final HttpHeaderMap aRequestHttpHeaders, @Nonnull final IAS4ResponseAbstraction aHttpResponse) throws Phase4Exception, IOException, MessagingException, WSSecurityException {
final IAS4ParsedMessageCallback aCallback = (aHttpHeaders, aSoapDocument, eSoapVersion, aIncomingAttachments) -> {
// SOAP document and SOAP version are determined
// Collect all runtime errors
final ICommonsList<Ebms3Error> aErrorMessages = new CommonsArrayList<>();
final IAS4ResponseFactory aResponder = _handleSoapMessage(aHttpHeaders, aSoapDocument, eSoapVersion, aIncomingAttachments, aErrorMessages);
if (aResponder != null) {
// Response present -> send back
final IAS4OutgoingDumper aRealOutgoingDumper = m_aOutgoingDumper != null ? m_aOutgoingDumper : AS4DumpManager.getOutgoingDumper();
aResponder.applyToResponse(aHttpResponse, aRealOutgoingDumper);
} else {
// Success, HTTP No Content
aHttpResponse.setStatus(CHttp.HTTP_NO_CONTENT);
}
AS4HttpDebug.debug(() -> "RECEIVE-END with " + (aResponder != null ? "EBMS message" : "no content"));
};
AS4IncomingHandler.parseAS4Message(m_aIAF, m_aResHelper, m_aMessageMetadata, aServletRequestIS, aRequestHttpHeaders, aCallback, m_aIncomingDumper);
}
use of com.helger.phase4.util.Phase4Exception in project phase4 by phax.
the class AS4RequestHandler method _handleSoapMessage.
@Nullable
private IAS4ResponseFactory _handleSoapMessage(@Nonnull final HttpHeaderMap aHttpHeaders, @Nonnull final Document aSoapDocument, @Nonnull final ESoapVersion eSoapVersion, @Nonnull final ICommonsList<WSS4JAttachment> aIncomingAttachments, @Nonnull final ICommonsList<Ebms3Error> aErrorMessagesTarget) throws WSSecurityException, MessagingException, Phase4Exception {
final SOAPHeaderElementProcessorRegistry aRegistry = SOAPHeaderElementProcessorRegistry.createDefault(m_aPModeResolver, m_aCryptoFactory, (IPMode) null);
final IAS4MessageState aState = AS4IncomingHandler.processEbmsMessage(m_aResHelper, m_aLocale, aRegistry, aHttpHeaders, aSoapDocument, eSoapVersion, aIncomingAttachments, m_aIncomingProfileSelector, aErrorMessagesTarget);
final IPMode aPMode = aState.getPMode();
final PModeLeg aEffectiveLeg = aState.getEffectivePModeLeg();
final String sMessageID = aState.getMessageID();
final ICommonsList<WSS4JAttachment> aDecryptedAttachments = aState.hasDecryptedAttachments() ? aState.getDecryptedAttachments() : aState.getOriginalAttachments();
final Node aPayloadNode = aState.getSoapBodyPayloadNode();
final Ebms3UserMessage aEbmsUserMessage = aState.getEbmsUserMessage();
final Ebms3SignalMessage aEbmsSignalMessage = aState.getEbmsSignalMessage();
if (aState.isSoapHeaderElementProcessingSuccessful()) {
final String sProfileID = aState.getProfileID();
if (LOGGER.isDebugEnabled())
LOGGER.debug("No checking for duplicate message with message ID '" + sMessageID + "' and profile ID '" + sProfileID + "'");
final boolean bIsDuplicate = MetaAS4Manager.getIncomingDuplicateMgr().registerAndCheck(sMessageID, sProfileID, aPMode == null ? null : aPMode.getID()).isBreak();
if (bIsDuplicate) {
LOGGER.error("Not invoking SPIs, because message with Message ID '" + sMessageID + "' was already handled!");
aErrorMessagesTarget.add(EEbmsError.EBMS_OTHER.getAsEbms3Error(m_aLocale, sMessageID, "Another message with the same Message ID '" + sMessageID + "' was already received!"));
} else {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Message is not a duplicate");
}
}
final SPIInvocationResult aSPIResult = new SPIInvocationResult();
// Storing for two-way response messages
final ICommonsList<WSS4JAttachment> aResponseAttachments = new CommonsArrayList<>();
// Invoke SPIs if
// * No errors so far (sign, encrypt, ...)
// * Valid PMode
// * Exactly one UserMessage or SignalMessage
// * No ping/test message
// * No Duplicate message ID
final boolean bCanInvokeSPIs = aErrorMessagesTarget.isEmpty() && !aState.isPingMessage();
if (bCanInvokeSPIs) {
// PMode may be null for receipts
if (aPMode == null || aPMode.getMEPBinding().isSynchronous() || aPMode.getMEPBinding().isAsynchronousInitiator() || aState.getEffectivePModeLegNumber() != 1) {
// Call synchronous
// Might add to aErrorMessages
// Might add to aResponseAttachments
// Might add to m_aPullReturnUserMsg
_invokeSPIsForIncoming(aHttpHeaders, aEbmsUserMessage, aEbmsSignalMessage, aPayloadNode, aDecryptedAttachments, aPMode, aState, aErrorMessagesTarget, aResponseAttachments, aSPIResult);
if (aSPIResult.isFailure())
LOGGER.warn("Error invoking synchronous SPIs");
else if (LOGGER.isDebugEnabled())
LOGGER.debug("Successfully invoked synchronous SPIs");
if (m_aSoapProcessingFinalizedCB != null)
m_aSoapProcessingFinalizedCB.onProcessingFinalized(true);
} else {
// Call asynchronous
// Only leg1 can be async!
final IThrowingRunnable<Exception> r = () -> {
// Start async
final ICommonsList<Ebms3Error> aLocalErrorMessages = new CommonsArrayList<>();
final ICommonsList<WSS4JAttachment> aLocalResponseAttachments = new CommonsArrayList<>();
final SPIInvocationResult aAsyncSPIResult = new SPIInvocationResult();
_invokeSPIsForIncoming(aHttpHeaders, aEbmsUserMessage, aEbmsSignalMessage, aPayloadNode, aDecryptedAttachments, aPMode, aState, aLocalErrorMessages, aLocalResponseAttachments, aAsyncSPIResult);
final IAS4ResponseFactory aAsyncResponseFactory;
final String sResponseMessageID;
if (aAsyncSPIResult.isSuccess()) {
// SPI processing succeeded
assert aLocalErrorMessages.isEmpty();
// The response user message has no explicit payload. All data of
// the response user message is in the local attachments
sResponseMessageID = MessageHelperMethods.createRandomMessageID();
final AS4UserMessage aResponseUserMsg = _createReversedUserMessage(eSoapVersion, sResponseMessageID, aEbmsUserMessage, aLocalResponseAttachments);
// Send UserMessage
final AS4SigningParams aSigningParams = new AS4SigningParams().setFromPMode(aEffectiveLeg.getSecurity());
// Use the original receiver ID as the alias into the keystore for
// encrypting the response message
final String sEncryptionAlias = aEbmsUserMessage.getPartyInfo().getTo().getPartyIdAtIndex(0).getValue();
final AS4CryptParams aCryptParams = new AS4CryptParams().setFromPMode(aEffectiveLeg.getSecurity()).setAlias(sEncryptionAlias);
aAsyncResponseFactory = _createResponseUserMessage(aState, aEffectiveLeg.getProtocol().getSoapVersion(), aResponseUserMsg, aResponseAttachments, aSigningParams, aCryptParams);
} else {
// SPI processing failed
// Send ErrorMessage Undefined - see
// https://github.com/phax/phase4/issues/4
final AS4ErrorMessage aResponseErrorMsg = AS4ErrorMessage.create(eSoapVersion, aState.getMessageID(), aLocalErrorMessages);
sResponseMessageID = aResponseErrorMsg.getEbms3SignalMessage().getMessageInfo().getMessageId();
// Pass error messages to the outside
if (m_aErrorConsumer != null && aLocalErrorMessages.isNotEmpty())
m_aErrorConsumer.onAS4ErrorMessage(aState, aLocalErrorMessages, aResponseErrorMsg);
aAsyncResponseFactory = new AS4ResponseFactoryXML(m_aMessageMetadata, aState, sResponseMessageID, aResponseErrorMsg.getAsSoapDocument(), eSoapVersion.getMimeType());
}
// where to send it back (must be determined by SPI!)
final String sAsyncResponseURL = aAsyncSPIResult.getAsyncResponseURL();
if (StringHelper.hasNoText(sAsyncResponseURL))
throw new IllegalStateException("No asynchronous response URL present - please check your SPI implementation");
if (LOGGER.isDebugEnabled())
LOGGER.debug("Responding asynchronous to: " + sAsyncResponseURL);
// Ensure HttpEntity is repeatable
HttpEntity aHttpEntity = aAsyncResponseFactory.getHttpEntityForSending(eSoapVersion.getMimeType());
aHttpEntity = m_aResHelper.createRepeatableHttpEntity(aHttpEntity);
// Use the prebuilt entity for dumping
_invokeSPIsForResponse(aState, aAsyncResponseFactory, aHttpEntity, eSoapVersion.getMimeType(), sResponseMessageID);
// invoke client with new document
final BasicHttpPoster aSender = new BasicHttpPoster();
final Document aAsyncResponse;
if (true) {
final HttpHeaderMap aResponseHttpHeaders = null;
// TODO make async send parameters customizable
final HttpRetrySettings aRetrySettings = new HttpRetrySettings();
aAsyncResponse = aSender.sendGenericMessageWithRetries(sAsyncResponseURL, aResponseHttpHeaders, aHttpEntity, sMessageID, aRetrySettings, new ResponseHandlerXml(), m_aOutgoingDumper, m_aRetryCallback);
} else {
aAsyncResponse = aSender.sendGenericMessage(sAsyncResponseURL, null, aHttpEntity, new ResponseHandlerXml());
}
AS4HttpDebug.debug(() -> "SEND-RESPONSE [async sent] received: " + XMLWriter.getNodeAsString(aAsyncResponse, AS4HttpDebug.getDebugXMLWriterSettings()));
};
final CompletableFuture<Void> aFuture = PhotonWorkerPool.getInstance().runThrowing(CAS4.LIB_NAME + " async processing", r);
if (m_aSoapProcessingFinalizedCB != null) {
// Give the outside world the possibility to get notified when the
// processing is done
aFuture.thenRun(() -> m_aSoapProcessingFinalizedCB.onProcessingFinalized(false));
}
}
}
// Try building error message
final String sResponseMessageID;
final IAS4ResponseFactory ret;
if (!aState.isSoapHeaderElementProcessingSuccessful() || aState.getEbmsError() == null) {
// Not an incoming Ebms Error Message
if (aErrorMessagesTarget.isNotEmpty()) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Creating AS4 error message with these " + aErrorMessagesTarget.size() + " errors: " + aErrorMessagesTarget.getAllMapped(Ebms3Error::getDescriptionValue));
final AS4ErrorMessage aResponseErrorMsg = AS4ErrorMessage.create(eSoapVersion, aState.getMessageID(), aErrorMessagesTarget);
// Call optional consumer
if (m_aErrorConsumer != null)
m_aErrorConsumer.onAS4ErrorMessage(aState, aErrorMessagesTarget, aResponseErrorMsg);
// When aLeg == null, the response is true
if (_isSendErrorAsResponse(aEffectiveLeg)) {
sResponseMessageID = aResponseErrorMsg.getEbms3SignalMessage().getMessageInfo().getMessageId();
ret = new AS4ResponseFactoryXML(m_aMessageMetadata, aState, sResponseMessageID, aResponseErrorMsg.getAsSoapDocument(), eSoapVersion.getMimeType());
} else {
LOGGER.warn("Not sending back the error, because sending error response is prohibited in PMode");
sResponseMessageID = null;
ret = null;
}
} else {
// Do not respond to receipt (except with error message - see above)
if (aEbmsSignalMessage == null || aEbmsSignalMessage.getReceipt() == null) {
// So now the incoming message is a user message or a pull request
if (aPMode.getMEP().isOneWay() || aPMode.getMEPBinding().isAsynchronous()) {
// the pull phase
if (aPMode.getMEPBinding().equals(EMEPBinding.PULL) || (aPMode.getMEPBinding().equals(EMEPBinding.PULL_PUSH) && aSPIResult.hasPullReturnUserMsg()) || (aPMode.getMEPBinding().equals(EMEPBinding.PUSH_PULL) && aSPIResult.hasPullReturnUserMsg())) {
// TODO would be nice to have attachments here I guess
final AS4UserMessage aResponseUserMsg = new AS4UserMessage(eSoapVersion, aSPIResult.getPullReturnUserMsg());
sResponseMessageID = aResponseUserMsg.getEbms3UserMessage().getMessageInfo().getMessageId();
ret = new AS4ResponseFactoryXML(m_aMessageMetadata, aState, sResponseMessageID, aResponseUserMsg.getAsSoapDocument(), eSoapVersion.getMimeType());
} else if (aEbmsUserMessage != null) {
// We received an incoming user message and no errors occurred
final boolean bSendReceiptAsResponse = _isSendReceiptAsResponse(aEffectiveLeg);
if (bSendReceiptAsResponse) {
sResponseMessageID = MessageHelperMethods.createRandomMessageID();
ret = _createResponseReceiptMessage(aState, aSoapDocument, eSoapVersion, sResponseMessageID, aEffectiveLeg, aEbmsUserMessage, aResponseAttachments);
} else {
// TODO what shall we send back here?
LOGGER.info("Not sending back the Receipt response, because sending Receipt response is prohibited in PMode");
sResponseMessageID = null;
ret = null;
}
} else {
sResponseMessageID = null;
ret = null;
}
} else {
// synchronous TWO - WAY (= "SYNC")
final PModeLeg aLeg2 = aPMode.getLeg2();
if (aLeg2 == null)
throw new Phase4Exception("PMode has no leg2!");
if (MEPHelper.isValidResponseTypeLeg2(aPMode.getMEP(), aPMode.getMEPBinding(), EAS4MessageType.USER_MESSAGE)) {
sResponseMessageID = MessageHelperMethods.createRandomMessageID();
final AS4UserMessage aResponseUserMsg = _createReversedUserMessage(eSoapVersion, sResponseMessageID, aEbmsUserMessage, aResponseAttachments);
final AS4SigningParams aSigningParams = new AS4SigningParams().setFromPMode(aLeg2.getSecurity());
final String sEncryptionAlias = aEbmsUserMessage.getPartyInfo().getTo().getPartyIdAtIndex(0).getValue();
final AS4CryptParams aCryptParams = new AS4CryptParams().setFromPMode(aLeg2.getSecurity()).setAlias(sEncryptionAlias);
ret = _createResponseUserMessage(aState, aLeg2.getProtocol().getSoapVersion(), aResponseUserMsg, aResponseAttachments, aSigningParams, aCryptParams);
} else {
sResponseMessageID = null;
ret = null;
}
}
} else {
sResponseMessageID = null;
ret = null;
}
}
} else {
sResponseMessageID = null;
ret = null;
}
// Create the HttpEntity on demand
_invokeSPIsForResponse(aState, ret, null, eSoapVersion.getMimeType(), sResponseMessageID);
return ret;
}
use of com.helger.phase4.util.Phase4Exception in project phase4 by phax.
the class AbstractAS4PullRequestBuilder method mainSendMessage.
@Override
protected final void mainSendMessage() throws Phase4Exception {
// Temporary file manager
try (final AS4ResourceHelper aResHelper = new AS4ResourceHelper()) {
// Start building AS4 User Message
final AS4ClientPullRequestMessage aPullRequestMsg = new AS4ClientPullRequestMessage(aResHelper);
applyToPullRequest(aPullRequestMsg);
// Main sending
AS4BidirectionalClientHelper.sendAS4PullRequestAndReceiveAS4UserMessage(m_aCryptoFactory, pmodeResolver(), incomingAttachmentFactory(), incomingProfileSelector(), aPullRequestMsg, m_aLocale, m_sEndpointURL, m_aBuildMessageCallback, m_aOutgoingDumper, m_aIncomingDumper, m_aRetryCallback, m_aResponseConsumer, m_aUserMsgConsumer);
} catch (final Phase4Exception ex) {
// Re-throw
throw ex;
} catch (final Exception ex) {
// wrap
throw new Phase4Exception("Wrapped Phase4Exception", ex);
}
}
use of com.helger.phase4.util.Phase4Exception in project phase4 by phax.
the class AbstractAS4UserMessageBuilder method sendMessageAndCheckForReceipt.
/**
* This is a sanity method that encapsulates all the sending checks that are
* necessary to determine overall sending success or error.<br>
* Note: this method is not thread-safe, because it changes the signal message
* consumer internally.
*
* @param aExceptionConsumer
* An optional Consumer that takes an eventually thrown
* {@link Phase4Exception}. May be <code>null</code>.
* @return {@link ESimpleUserMessageSendResult#SUCCESS} only if all parameters
* are correct, HTTP transmission was successful and if a positive AS4
* Receipt was returned. Never <code>null</code>.
* @since 1.0.0-rc1
*/
@Nonnull
public final ESimpleUserMessageSendResult sendMessageAndCheckForReceipt(@Nullable final Consumer<? super Phase4Exception> aExceptionConsumer) {
final IAS4SignalMessageConsumer aOld = m_aSignalMsgConsumer;
try {
// Store the received signal message
final Wrapper<Ebms3SignalMessage> aSignalMsgKeeper = new Wrapper<>();
m_aSignalMsgConsumer = aOld == null ? aSignalMsgKeeper::set : x -> {
aSignalMsgKeeper.set(x);
aOld.handleSignalMessage(x);
};
// Main sending
if (sendMessage().isFailure()) {
// Parameters are missing/incorrect
return ESimpleUserMessageSendResult.INVALID_PARAMETERS;
}
final Ebms3SignalMessage aSignalMsg = aSignalMsgKeeper.get();
if (aSignalMsg == null) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("Failed to get a SignalMessage as the response");
// Unexpected response - invalid XML or at least no Ebms3 signal message
return ESimpleUserMessageSendResult.NO_SIGNAL_MESSAGE_RECEIVED;
}
if (aSignalMsg.hasErrorEntries()) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("The received SignalMessage contains at lease one error");
// Errors have precedence over receipts
return ESimpleUserMessageSendResult.AS4_ERROR_MESSAGE_RECEIVED;
}
if (aSignalMsg.getReceipt() != null) {
// A receipt was returned - this is deemed success
return ESimpleUserMessageSendResult.SUCCESS;
}
if (LOGGER.isDebugEnabled())
LOGGER.debug("The SignalMessage contains neither Errors nor a Receipt - unexpected SignalMessage layout.");
// Neither an error nor a receipt was returned - this is weird
return ESimpleUserMessageSendResult.INVALID_SIGNAL_MESSAGE_RECEIVED;
} catch (final Phase4Exception ex) {
if (LOGGER.isDebugEnabled())
LOGGER.debug("An exception occurred sending out the AS4 message", ex);
if (aExceptionConsumer != null)
aExceptionConsumer.accept(ex);
// Something went wrong - see the logs
return ESimpleUserMessageSendResult.TRANSPORT_ERROR;
} finally {
// Restore the original value
m_aSignalMsgConsumer = aOld;
}
}
Aggregations