use of org.eclipse.microprofile.lra.annotation.ParticipantStatus in project helidon by oracle.
the class Participant method sendComplete.
boolean sendComplete(Lra lra) {
Optional<URI> endpointURI = getCompleteURI();
for (AtomicInteger i = new AtomicInteger(0); i.getAndIncrement() < SYNCHRONOUS_RETRY_CNT; ) {
if (!sendingStatus.compareAndSet(SendingStatus.NOT_SENDING, SendingStatus.SENDING))
return false;
LOGGER.log(Level.FINE, () -> "Sending complete, sync retry: " + i.get() + ", status: " + status.get().name() + " statusUri: " + getStatusURI().map(URI::toASCIIString).orElse(null));
WebClientResponse response = null;
try {
if (status.get().isFinal()) {
return true;
// call for client status only on retries and when status uri is known
} else if (!status.get().equals(Status.ACTIVE) && getStatusURI().isPresent()) {
// If the participant does not support idempotency then it MUST be able to report its status
// by annotating one of the methods with the @Status annotation which should report the status
// in case we can't retrieve status from participant just retry n times
ParticipantStatus reportedClientStatus = retrieveStatus(lra, Completing).orElse(null);
if (reportedClientStatus == Completed) {
LOGGER.log(Level.INFO, "Participant reports it is completed.");
status.set(Status.COMPLETED);
return true;
} else if (reportedClientStatus == FailedToComplete) {
LOGGER.log(Level.INFO, "Participant reports it failed to complete.");
status.set(Status.FAILED_TO_COMPLETE);
return true;
} else if (reportedClientStatus == Active) {
// last call didn't reach participant, try call again
} else if (reportedClientStatus == Completing) {
LOGGER.log(Level.INFO, "Participant reports it is still completing.");
status.set(Status.CLIENT_COMPLETING);
return false;
} else if (remainingCloseAttempts.decrementAndGet() <= 0) {
LOGGER.log(Level.INFO, "Participant didnt report final status after {0} status call retries.", new Object[] { RETRY_CNT });
status.set(Status.FAILED_TO_COMPLETE);
return true;
} else {
// Unknown status, lets try in next recovery cycle
return false;
}
}
response = getWebClient(endpointURI.get()).put().headers(lra.headers()).submit(LRAStatus.Closed.name()).await(timeout, TimeUnit.MILLISECONDS);
switch(response.status().code()) {
// complete or compensated
case 200:
case 410:
status.set(Status.COMPLETED);
return true;
// retryable
case 202:
// Still completing, check with @Status later
this.status.set(Status.CLIENT_COMPLETING);
return false;
case 409:
case 404:
case 503:
default:
throw new Exception(response.status().code() + " " + response.status().reasonPhrase());
}
} catch (Exception e) {
LOGGER.log(Level.WARNING, e, () -> "Can't reach participant's complete endpoint: " + endpointURI.map(URI::toASCIIString).orElse("unknown"));
if (remainingCloseAttempts.decrementAndGet() <= 0) {
LOGGER.log(Level.WARNING, "Failed to complete participant of LRA {0} {1} {2}", new Object[] { lra.lraId(), this.getCompleteURI(), e.getMessage() });
status.set(Status.FAILED_TO_COMPLETE);
} else {
status.set(Status.COMPLETING);
}
} finally {
Optional.ofNullable(response).ifPresent(WebClientResponse::close);
sendingStatus.set(SendingStatus.NOT_SENDING);
}
}
return false;
}
use of org.eclipse.microprofile.lra.annotation.ParticipantStatus in project helidon by oracle.
the class Participant method retrieveStatus.
Optional<ParticipantStatus> retrieveStatus(Lra lra, ParticipantStatus inProgressStatus) {
URI statusURI = this.getStatusURI().get();
try {
WebClientResponse response = getWebClient(statusURI).get().headers(h -> {
// Dont send parent!
h.add(LRA_HTTP_CONTEXT_HEADER, lra.lraContextId());
h.add(LRA_HTTP_RECOVERY_HEADER, lra.lraContextId() + "/recovery");
h.add(LRA_HTTP_ENDED_CONTEXT_HEADER, lra.lraContextId());
return h;
}).request().await(timeout, TimeUnit.MILLISECONDS);
int code = response.status().code();
switch(code) {
case 202:
return Optional.of(inProgressStatus);
case // GONE
410:
// Completing -> FailedToComplete ...
return status.get().failedFinalStatus();
case 503:
case 500:
throw new IllegalStateException(String.format("Client reports unexpected status %s %s, " + "current participant state is %s, " + "lra: %s " + "status uri: %s", code, response.content().as(String.class), status.get(), lra.lraId(), statusURI.toASCIIString()));
default:
ParticipantStatus reportedStatus = valueOf(response.content().as(String.class).await());
Status currentStatus = status.get();
if (currentStatus.validateNextStatus(reportedStatus)) {
return Optional.of(reportedStatus);
} else {
LOGGER.log(Level.WARNING, "Client reports unexpected status {0} {1}, " + "current participant state is {2}, " + "lra: {3} " + "status uri: {4}", new Object[] { code, reportedStatus, currentStatus, lra.lraId(), statusURI.toASCIIString() });
return Optional.empty();
}
}
} catch (Exception e) {
LOGGER.log(Level.WARNING, "Error when getting participant status. " + statusURI, e);
// skip dependent compensation call, another retry with status call might be luckier
throw e;
}
}
use of org.eclipse.microprofile.lra.annotation.ParticipantStatus in project helidon by oracle.
the class NonJaxRsResource method createNonJaxRsParticipantResource.
Service createNonJaxRsParticipantResource() {
return rules -> rules.any("/{type}/{fqdn}/{methodName}", (req, res) -> {
LOGGER.log(Level.FINE, () -> "Non JAX-RS LRA resource " + req.method().name() + " " + req.absoluteUri());
RequestHeaders headers = req.headers();
HttpRequest.Path path = req.path();
URI lraId = headers.first(LRA_HTTP_CONTEXT_HEADER).or(() -> headers.first(LRA_HTTP_ENDED_CONTEXT_HEADER)).map(URI::create).orElse(null);
URI parentId = headers.first(LRA_HTTP_PARENT_CONTEXT_HEADER).map(URI::create).orElse(null);
PropagatedHeaders propagatedHeaders = participantService.prepareCustomHeaderPropagation(headers.toMap());
String fqdn = path.param("fqdn");
String method = path.param("methodName");
String type = path.param("type");
switch(type) {
case "compensate":
case "complete":
Single.<Optional<?>>empty().observeOn(exec).onCompleteResumeWithSingle(o -> participantService.invoke(fqdn, method, lraId, parentId, propagatedHeaders)).forSingle(result -> result.ifPresentOrElse(r -> sendResult(res, r), res::send)).exceptionallyAccept(t -> sendError(lraId, req, res, t));
break;
case "afterlra":
req.content().as(String.class).map(LRAStatus::valueOf).observeOn(exec).flatMapSingle(s -> Single.defer(() -> participantService.invoke(fqdn, method, lraId, s, propagatedHeaders))).onComplete(res::send).onError(t -> sendError(lraId, req, res, t)).ignoreElement();
break;
case "status":
Single.<Optional<?>>empty().observeOn(exec).onCompleteResumeWithSingle(o -> participantService.invoke(fqdn, method, lraId, null, propagatedHeaders)).forSingle(result -> result.ifPresentOrElse(r -> sendResult(res, r), // or in the case of non-JAX-RS method returning ParticipantStatus null.
() -> res.status(Response.Status.GONE.getStatusCode()).send())).exceptionallyAccept(t -> sendError(lraId, req, res, t));
break;
case "forget":
Single.<Optional<?>>empty().observeOn(exec).onCompleteResumeWithSingle(o -> participantService.invoke(fqdn, method, lraId, parentId, propagatedHeaders)).onComplete(res::send).onError(t -> sendError(lraId, req, res, t)).ignoreElement();
break;
default:
LOGGER.severe(() -> "Unexpected non Jax-Rs LRA compensation type " + type + ": " + req.absoluteUri());
res.status(404).send();
break;
}
});
}
use of org.eclipse.microprofile.lra.annotation.ParticipantStatus in project helidon by oracle.
the class Participant method sendCancel.
boolean sendCancel(Lra lra) {
Optional<URI> endpointURI = getCompensateURI();
for (AtomicInteger i = new AtomicInteger(0); i.getAndIncrement() < SYNCHRONOUS_RETRY_CNT; ) {
if (!sendingStatus.compareAndSet(SendingStatus.NOT_SENDING, SendingStatus.SENDING))
return false;
if (!compensateCalled.compareAndSet(CompensateStatus.NOT_SENT, CompensateStatus.SENDING))
return false;
LOGGER.log(Level.FINE, () -> "Sending compensate, sync retry: " + i.get() + ", status: " + status.get().name() + " statusUri: " + getStatusURI().map(URI::toASCIIString).orElse(null));
WebClientResponse response = null;
try {
// call for client status only on retries and when status uri is known
if (!status.get().equals(Status.ACTIVE) && getStatusURI().isPresent()) {
// If the participant does not support idempotency then it MUST be able to report its status
// by annotating one of the methods with the @Status annotation which should report the status
// in case we can't retrieve status from participant just retry n times
ParticipantStatus reportedClientStatus = retrieveStatus(lra, Compensating).orElse(null);
if (reportedClientStatus == Compensated) {
LOGGER.log(Level.INFO, "Participant reports it is compensated.");
status.set(Status.COMPENSATED);
return true;
} else if (reportedClientStatus == FailedToCompensate) {
LOGGER.log(Level.INFO, "Participant reports it failed to compensate.");
status.set(Status.FAILED_TO_COMPENSATE);
return true;
} else if (reportedClientStatus == Active) {
// last call didn't reach participant, try call again
} else if (reportedClientStatus == Completed && lra.isChild()) {
// completed participant can be compensated again in case of nested tx
} else if (reportedClientStatus == Compensating) {
LOGGER.log(Level.INFO, "Participant reports it is still compensating.");
status.set(Status.CLIENT_COMPENSATING);
return false;
} else if (remainingCloseAttempts.decrementAndGet() <= 0) {
LOGGER.log(Level.INFO, "Participant didnt report final status after {0} status call retries.", new Object[] { RETRY_CNT });
status.set(Status.FAILED_TO_COMPENSATE);
return true;
} else {
// Unknown status, lets try in next recovery cycle
LOGGER.log(Level.INFO, "Unknown status of " + lra.lraId());
return false;
}
}
response = getWebClient(endpointURI.get()).put().headers(lra.headers()).submit(LRAStatus.Cancelled.name()).await(timeout, TimeUnit.MILLISECONDS);
switch(response.status().code()) {
// complete or compensated
case 200:
case 410:
LOGGER.log(Level.INFO, "Compensated participant of LRA {0} {1}", new Object[] { lra.lraId(), this.getCompensateURI() });
status.set(Status.COMPENSATED);
compensateCalled.set(CompensateStatus.SENT);
return true;
// retryable
case 202:
// Still compensating, check with @Status later
this.status.set(Status.CLIENT_COMPENSATING);
return false;
case 409:
case 404:
case 503:
default:
throw new Exception(response.status().code() + " " + response.status().reasonPhrase());
}
} catch (Exception e) {
LOGGER.log(Level.WARNING, e, () -> "Can't reach participant's compensate endpoint: " + endpointURI.map(URI::toASCIIString).orElse("unknown"));
if (remainingCloseAttempts.decrementAndGet() <= 0) {
LOGGER.log(Level.WARNING, "Failed to compensate participant of LRA {0} {1} {2}", new Object[] { lra.lraId(), this.getCompensateURI(), e.getMessage() });
status.set(Status.FAILED_TO_COMPENSATE);
} else {
status.set(Status.COMPENSATING);
}
} finally {
Optional.ofNullable(response).ifPresent(WebClientResponse::close);
sendingStatus.set(SendingStatus.NOT_SENDING);
compensateCalled.compareAndSet(CompensateStatus.SENDING, CompensateStatus.NOT_SENT);
}
}
return false;
}
Aggregations