Skip to content

Commit

Permalink
Merge pull request #128 from mmusgrov/issue120
Browse files Browse the repository at this point in the history
issue120 test the behaviour of the LRA#type annotation

@rdebusscher I addressed all of your comments and the other reviewer has approved so I am going ahead and merging.
  • Loading branch information
mmusgrov authored Apr 1, 2019
2 parents f250f87 + 7414a8c commit 86c025b
Show file tree
Hide file tree
Showing 5 changed files with 612 additions and 9 deletions.
130 changes: 121 additions & 9 deletions tck/src/main/java/org/eclipse/microprofile/lra/tck/LRAClientOps.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,14 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;

import static junit.framework.TestCase.fail;
import static org.eclipse.microprofile.lra.tck.participant.api.NonParticipatingTckResource.END_PATH;
Expand All @@ -41,8 +49,11 @@
import static org.eclipse.microprofile.lra.tck.participant.api.ParticipatingTckResource.TCK_PARTICIPANT_RESOURCE_PATH;

public class LRAClientOps {
private static final Logger LOGGER = Logger.getLogger(TckLRATypeTests.class.getName());

/**
* TODO recovery needs to be properly speced
* TODO recovery needs to be properly speced: see the proposal
* on issue https://github.com/eclipse/microprofile-lra/issues/116
* It is used in the two tests for participants that return 202:
* {@link TckTests#acceptCancelTest()} and {@link TckTests#acceptCloseTest()}
* to connect to an endpoint that will trigger recovery (the alternative would
Expand All @@ -68,9 +79,13 @@ public class LRAClientOps {
static final String LRA_RECOVERY_PATH_KEY = "lra.http.recovery.path";

private final WebTarget target;
private final ScheduledExecutorService executor;
private final Map<LRATask, ScheduledFuture<?>> lraTasks;

public LRAClientOps(WebTarget target) {
this.target = target;
this.executor = Executors.newSingleThreadScheduledExecutor();
this.lraTasks = new HashMap<>();
}

// see if it is possible to join with an LRA - if it is possible to do that then the LRA is still active
Expand Down Expand Up @@ -105,7 +120,8 @@ private Response invokeRestEndpoint(URI lra, String basePath, String path, int c
return invokeRestEndpoint(lra == null ? null : lra.toASCIIString(), basePath, path, coerceResponse);
}

private Response invokeRestEndpoint(String lra, String basePath, String path, int coerceResponse) {
// synchronize access to the connection since it is shared with the LRA background cancellation code
private synchronized Response invokeRestEndpoint(String lra, String basePath, String path, int coerceResponse) {
WebTarget resourcePath = target.path(basePath).path(path).queryParam(STATUS_CODE_QUERY_NAME, coerceResponse);
Invocation.Builder builder = resourcePath.request();

Expand Down Expand Up @@ -144,26 +160,122 @@ private URI toURI(String lra) throws GenericLRAException {
}
}

public URI startLRA(URI parentLRA, String clientID, Long timeout, ChronoUnit unit)
public URI startLRA(URI parentLRA, String clientID, long timeout, ChronoUnit unit)
throws GenericLRAException {
return toURI(invokeRestEndpoint(parentLRA,
String lra = invokeRestEndpoint(parentLRA,
TCK_NON_PARTICIPANT_RESOURCE_PATH, START_BUT_DONT_END_PATH, 200)
.readEntity(String.class));
}
.readEntity(String.class);

void cancelLRA(String lra) {
invokeRestEndpointAndReturnLRA(lra, TCK_NON_PARTICIPANT_RESOURCE_PATH, END_PATH, 200);
if (timeout > 0L) {
scheduleCancelation(clientID, toURI(lra), timeout, unit);
}

return toURI(lra);
}

void cancelLRA(URI lraId) throws GenericLRAException {
cancelCancelation(lraId);

invokeRestEndpointAndReturnLRA(lraId.toASCIIString(), TCK_NON_PARTICIPANT_RESOURCE_PATH, END_PATH, 500);
}

private void cancelLRA(String clientId, URI lra) {
LOGGER.warning("cancelling LRA from the timer: clientId: " + clientId + " LRA id: " + lra.toASCIIString());
cancelLRA(lra);
throw new IllegalArgumentException("LRA timed out prematurely");
}

void closeLRA(URI lraId) throws GenericLRAException {
cancelCancelation(lraId);

invokeRestEndpointAndReturnLRA(lraId.toASCIIString(), TCK_NON_PARTICIPANT_RESOURCE_PATH, END_PATH, 200);
}

void leaveLRA(URI lra, String basePath, String resourcePath) throws GenericLRAException {
void closeLRA(String lraId) {
closeLRA(toURI(lraId));
}

private void leaveLRA(URI lra, String basePath, String resourcePath) throws GenericLRAException {
invokeRestEndpoint(lra, basePath, resourcePath, 200);
}

/*
* Include support for timing out LRAs. A timed out LRA will generally produce a test failure.
*/

private static TimeUnit timeUnit(ChronoUnit unit) {
switch (unit) {
case NANOS:
return TimeUnit.NANOSECONDS;
case MICROS:
return TimeUnit.MICROSECONDS;
case MILLIS:
return TimeUnit.MILLISECONDS;
case SECONDS:
return TimeUnit.SECONDS;
case MINUTES:
return TimeUnit.MINUTES;
case HOURS:
return TimeUnit.HOURS;
case DAYS:
return TimeUnit.DAYS;
default:
throw new IllegalArgumentException("ChronoUnit cannot be converted to TimeUnit: " + unit);
}
}

/**
* arrange for an LRA to be automatically cancelled after a specified timeout
*
* @param clientId client assigned arbitrary identifier for the LRA
* @param lra the LRA that should be cancelled when the time limit is reached
* @param timeout the time to wait before attempting cancellation of the LRA
* @param unit the time unit
*/
private void scheduleCancelation(String clientId, URI lra, long timeout, ChronoUnit unit) {
lraTasks.put(new LRATask(clientId, lra), executor.schedule(() -> cancelLRA(clientId, lra), timeout, timeUnit(unit)));
}

private void cancelCancelation(URI lraId) {
LRATask lra = new LRATask(null, lraId);

if (lraTasks.containsKey(lra)) {
lraTasks.remove(lra).cancel(false);
}
}

void cleanUp(Logger logger, String testName) {
lraTasks.forEach((lra, future) -> {
logger.warning("Test: " + testName + " didn't finish LRA " + lra.lra + " with clientId " + lra.clientId);
cancelLRA(lra.lra);
});
}

// class to store the clientId associated with an LRA
private class LRATask {
final String clientId; // client assigned arbitrary identifier for the LRA
final URI lra; // the LRA that clientId is associated with

LRATask(String clientId, URI lra) {
this.clientId = clientId;
this.lra = lra;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
LRATask lraTask = (LRATask) o;
return lra.equals(lraTask.lra);
}

@Override
public int hashCode() {
return Objects.hash(lra);
}
}
}
Loading

0 comments on commit 86c025b

Please sign in to comment.