use of com.helger.phase4.client.IAS4RetryCallback in project phase4 by phax.
the class AbstractAS4Client method sendMessageWithRetries.
/**
* Send the AS4 client message created by
* {@link #buildMessage(String, IAS4ClientBuildMessageCallback)} to the
* provided URL. This methods does take retries into account. It synchronously
* handles the retries and only returns after the last retry.
*
* @param <T>
* The response data type
* @param sURL
* The URL to send the HTTP POST to
* @param aResponseHandler
* The response handler that converts the HTTP response to a domain
* object. May not be <code>null</code>.
* @param aCallback
* An optional callback for the different stages of building the
* document. May be <code>null</code>.
* @param aOutgoingDumper
* An outgoing dumper to be used. Maybe <code>null</code>. If
* <code>null</code> the global outgoing dumper from
* {@link AS4DumpManager} is used.
* @param aRetryCallback
* An optional callback to be invoked if a retry happens on HTTP level.
* May be <code>null</code>.
* @return The sent message that contains
* @throws IOException
* in case of error when building or sending the message
* @throws WSSecurityException
* In case there is an issue with signing/encryption
* @throws MessagingException
* in case something happens in MIME wrapping
* @since 0.9.14
*/
@Nonnull
public final <T> AS4ClientSentMessage<T> sendMessageWithRetries(@Nonnull final String sURL, @Nonnull final ResponseHandler<? extends T> aResponseHandler, @Nullable final IAS4ClientBuildMessageCallback aCallback, @Nullable final IAS4OutgoingDumper aOutgoingDumper, @Nullable final IAS4RetryCallback aRetryCallback) throws IOException, WSSecurityException, MessagingException {
// Create a new message ID for each build!
final String sMessageID = createMessageID();
final AS4ClientBuiltMessage aBuiltMsg = buildMessage(sMessageID, aCallback);
HttpEntity aBuiltEntity = aBuiltMsg.getHttpEntity();
final HttpHeaderMap aBuiltHttpHeaders = aBuiltMsg.getCustomHeaders();
if (m_aHttpRetrySettings.isRetryEnabled() || aOutgoingDumper != null || AS4DumpManager.getOutgoingDumper() != null) {
// Ensure a repeatable entity is provided
aBuiltEntity = m_aResHelper.createRepeatableHttpEntity(aBuiltEntity);
}
// Keep the HTTP response status line for external evaluation
final Wrapper<StatusLine> aStatusLineKeeper = new Wrapper<>();
// Keep the HTTP response headers for external evaluation
final HttpHeaderMap aResponseHeaders = new HttpHeaderMap();
final ResponseHandler<T> aRealResponseHandler = x -> {
// Remember the HTTP response data
aStatusLineKeeper.set(x.getStatusLine());
final Header[] aHeaders = x.getAllHeaders();
if (aHeaders != null)
for (final Header aHeader : aHeaders) aResponseHeaders.addHeader(aHeader.getName(), aHeader.getValue());
// Call the original handler
return aResponseHandler.handleResponse(x);
};
final T aResponseContent = m_aHttpPoster.sendGenericMessageWithRetries(sURL, aBuiltHttpHeaders, aBuiltEntity, sMessageID, m_aHttpRetrySettings, aRealResponseHandler, aOutgoingDumper, aRetryCallback);
return new AS4ClientSentMessage<>(aBuiltMsg, aStatusLineKeeper.get(), aResponseHeaders, aResponseContent);
}
use of com.helger.phase4.client.IAS4RetryCallback in project phase4 by phax.
the class AbstractAS4Client method sendMessageAndGetMicroDocument.
@Nullable
public IMicroDocument sendMessageAndGetMicroDocument(@Nonnull final String sURL) throws WSSecurityException, IOException, MessagingException {
final IAS4ClientBuildMessageCallback aCallback = null;
final IAS4OutgoingDumper aOutgoingDumper = null;
final IAS4RetryCallback aRetryCallback = null;
final IMicroDocument ret = sendMessageWithRetries(sURL, new ResponseHandlerMicroDom(), aCallback, aOutgoingDumper, aRetryCallback).getResponse();
AS4HttpDebug.debug(() -> "SEND-RESPONSE received: " + MicroWriter.getNodeAsString(ret, AS4HttpDebug.getDebugXMLWriterSettings()));
return ret;
}
use of com.helger.phase4.client.IAS4RetryCallback in project phase4 by phax.
the class AS4BidirectionalClientHelper method sendAS4PullRequestAndReceiveAS4UserMessage.
public static void sendAS4PullRequestAndReceiveAS4UserMessage(@Nonnull final IAS4CryptoFactory aCryptoFactory, @Nonnull final IPModeResolver aPModeResolver, @Nonnull final IAS4IncomingAttachmentFactory aIAF, @Nonnull final IAS4IncomingProfileSelector aIncomingProfileSelector, @Nonnull final AS4ClientPullRequestMessage aClientPullRequest, @Nonnull final Locale aLocale, @Nonnull final String sURL, @Nullable final IAS4ClientBuildMessageCallback aBuildMessageCallback, @Nullable final IAS4OutgoingDumper aOutgoingDumper, @Nullable final IAS4IncomingDumper aIncomingDumper, @Nullable final IAS4RetryCallback aRetryCallback, @Nullable final IAS4RawResponseConsumer aResponseConsumer, @Nullable final IAS4UserMessageConsumer aUserMsgConsumer) throws IOException, Phase4Exception, WSSecurityException, MessagingException {
if (LOGGER.isInfoEnabled())
LOGGER.info("Sending AS4 PullRequest to '" + sURL + "' with max. " + aClientPullRequest.httpRetrySettings().getMaxRetries() + " retries");
if (LOGGER.isDebugEnabled())
LOGGER.debug(" MPC = '" + aClientPullRequest.getMPC() + "'");
final Wrapper<HttpResponse> aWrappedResponse = new Wrapper<>();
final ResponseHandler<byte[]> aResponseHdl = aHttpResponse -> {
// May throw an ExtendedHttpResponseException
final HttpEntity aEntity = ResponseHandlerHttpEntity.INSTANCE.handleResponse(aHttpResponse);
if (aEntity == null)
return null;
aWrappedResponse.set(aHttpResponse);
return EntityUtils.toByteArray(aEntity);
};
final AS4ClientSentMessage<byte[]> aResponseEntity = aClientPullRequest.sendMessageWithRetries(sURL, aResponseHdl, aBuildMessageCallback, aOutgoingDumper, aRetryCallback);
if (LOGGER.isInfoEnabled())
LOGGER.info("Successfully transmitted AS4 PullRequest 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) {
final IAS4IncomingMessageMetadata aMessageMetadata = new AS4IncomingMessageMetadata(EAS4MessageMode.RESPONSE).setRemoteAddr(sURL);
// Read response as EBMS3 User Message
// Read it in any case to ensure signature validation etc. happens
final Ebms3UserMessage aUserMessage = AS4IncomingHandler.parseUserMessage(aCryptoFactory, aPModeResolver, aIAF, aIncomingProfileSelector, aClientPullRequest.getAS4ResourceHelper(), null, aLocale, aMessageMetadata, aWrappedResponse.get(), aResponseEntity.getResponse(), aIncomingDumper);
if (aUserMessage != null && aUserMsgConsumer != null)
aUserMsgConsumer.handleUserMessage(aUserMessage);
} else
LOGGER.info("AS4 ResponseEntity is empty");
}
use of com.helger.phase4.client.IAS4RetryCallback in project phase4 by phax.
the class AS4BidirectionalClientHelper method sendAS4UserMessageAndReceiveAS4SignalMessage.
public static void sendAS4UserMessageAndReceiveAS4SignalMessage(@Nonnull final IAS4CryptoFactory aCryptoFactory, @Nonnull final IPModeResolver aPModeResolver, @Nonnull final IAS4IncomingAttachmentFactory aIAF, @Nonnull final IAS4IncomingProfileSelector aIncomingProfileSelector, @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 IAS4RetryCallback aRetryCallback, @Nullable final IAS4RawResponseConsumer aResponseConsumer, @Nullable final IAS4SignalMessageConsumer aSignalMsgConsumer) throws IOException, Phase4Exception, WSSecurityException, MessagingException {
if (LOGGER.isInfoEnabled())
LOGGER.info("Sending AS4 UserMessage to '" + sURL + "' with max. " + aClientUserMsg.httpRetrySettings().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<HttpResponse> aWrappedResponse = new Wrapper<>();
final ResponseHandler<byte[]> aResponseHdl = aHttpResponse -> {
// throws an ExtendedHttpResponseException on exception
final HttpEntity aEntity = ResponseHandlerHttpEntity.INSTANCE.handleResponse(aHttpResponse);
if (aEntity == null)
return null;
aWrappedResponse.set(aHttpResponse);
return EntityUtils.toByteArray(aEntity);
};
final AS4ClientSentMessage<byte[]> aResponseEntity = aClientUserMsg.sendMessageWithRetries(sURL, aResponseHdl, aBuildMessageCallback, aOutgoingDumper, aRetryCallback);
if (LOGGER.isInfoEnabled())
LOGGER.info("Successfully transmitted AS4 UserMessage 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) {
final IAS4IncomingMessageMetadata aMessageMetadata = new AS4IncomingMessageMetadata(EAS4MessageMode.RESPONSE).setRemoteAddr(sURL);
// Read response as EBMS3 Signal Message
// Read it in any case to ensure signature validation etc. happens
final Ebms3SignalMessage aSignalMessage = AS4IncomingHandler.parseSignalMessage(aCryptoFactory, aPModeResolver, aIAF, aIncomingProfileSelector, aClientUserMsg.getAS4ResourceHelper(), aClientUserMsg.getPMode(), aLocale, aMessageMetadata, aWrappedResponse.get(), aResponseEntity.getResponse(), aIncomingDumper);
if (aSignalMessage != null && aSignalMsgConsumer != null)
aSignalMsgConsumer.handleSignalMessage(aSignalMessage);
} else
LOGGER.info("AS4 ResponseEntity is empty");
}
use of com.helger.phase4.client.IAS4RetryCallback in project phase4 by phax.
the class BasicHttpPoster method sendGenericMessageWithRetries.
@Nonnull
public <T> T sendGenericMessageWithRetries(@Nonnull final String sURL, @Nullable final HttpHeaderMap aCustomHttpHeaders, @Nonnull final HttpEntity aHttpEntity, @Nonnull final String sMessageID, @Nonnull final HttpRetrySettings aRetrySettings, @Nonnull final ResponseHandler<? extends T> aResponseHandler, @Nullable final IAS4OutgoingDumper aOutgoingDumper, @Nullable final IAS4RetryCallback aRetryCallback) throws IOException {
// Parameter or global one - may still be null
final IAS4OutgoingDumper aRealOutgoingDumper = aOutgoingDumper != null ? aOutgoingDumper : AS4DumpManager.getOutgoingDumper();
// This class holds the effective OutputStream to which the dump is written
final Wrapper<OutputStream> aDumpOSHolder = new Wrapper<>();
try {
if (aRetrySettings.isRetryEnabled()) {
// Send with retry
if (!aHttpEntity.isRepeatable())
throw new IllegalStateException("If retry is enabled, a repeatable entity must be provided");
final int nMaxRetries = aRetrySettings.getMaxRetries();
final int nMaxTries = 1 + nMaxRetries;
Duration aDurationBeforeRetry = aRetrySettings.getDurationBeforeRetry();
for (int nTry = 0; nTry < nMaxTries; nTry++) {
if (nTry > 0)
LOGGER.info("Retry #" + nTry + "/" + nMaxRetries + " for sending message with ID '" + sMessageID + "'");
try {
// Create a new one every time (for new filename, new timestamp,
// etc.)
final HttpEntity aDumpingEntity = createDumpingHttpEntity(aRealOutgoingDumper, aHttpEntity, sMessageID, aCustomHttpHeaders, nTry, aDumpOSHolder);
// Dump only for the first try - the remaining tries
return sendGenericMessage(sURL, aCustomHttpHeaders, aDumpingEntity, aResponseHandler);
} catch (final IOException ex) {
// Last try? -> propagate exception
if (nTry == nMaxTries - 1)
throw ex;
// After the first retry, increase the waiting time
if (nTry > 1)
aDurationBeforeRetry = HttpRetrySettings.getIncreased(aDurationBeforeRetry, aRetrySettings.getRetryIncreaseFactor());
if (aRetryCallback != null)
if (aRetryCallback.onBeforeRetry(sMessageID, sURL, nTry, nMaxTries, aDurationBeforeRetry.toMillis(), ex).isBreak()) {
// Explicitly interrupt retry
LOGGER.warn("Error sending message '" + sMessageID + "' to '" + sURL + ": " + ex.getClass().getSimpleName() + " - " + ex.getMessage() + " - retrying was explicitly stopped by the RetryCallback");
// Propagate Exception as if it would be the last retry
throw ex;
}
LOGGER.warn("Error sending message '" + sMessageID + "' to '" + sURL + "': " + ex.getClass().getSimpleName() + " - " + ex.getMessage() + " - waiting " + aDurationBeforeRetry.toMillis() + " ms, than retrying");
// Sleep and try again afterwards
ThreadHelper.sleep(aDurationBeforeRetry.toMillis());
} finally {
// Flush and close the dump output stream (if any)
StreamHelper.close(aDumpOSHolder.get());
}
}
throw new IllegalStateException("Should never be reached (after maximum of " + nMaxTries + " tries)!");
}
// else non retry
{
final HttpEntity aDumpingEntity = createDumpingHttpEntity(aRealOutgoingDumper, aHttpEntity, sMessageID, aCustomHttpHeaders, 0, aDumpOSHolder);
try {
// Send without retry
return sendGenericMessage(sURL, aCustomHttpHeaders, aDumpingEntity, aResponseHandler);
} finally {
// Close the dump output stream (if any)
StreamHelper.close(aDumpOSHolder.get());
}
}
} finally {
// Add the possibility to close open resources
if (aRealOutgoingDumper != null && aDumpOSHolder.isSet())
try {
aRealOutgoingDumper.onEndRequest(EAS4MessageMode.REQUEST, null, null, sMessageID);
} catch (final Exception ex) {
LOGGER.error("OutgoingDumper.onEndRequest failed. Dumper=" + aRealOutgoingDumper + "; MessageID=" + sMessageID, ex);
}
}
}
Aggregations