Skip to content

Add forced IAM token refresh API to the Java toolkit - #17

Merged
alexr-bq merged 3 commits into
release-2from
add-forced-iam-token-refresh-api-to-the-ja-e7facdbe
Sep 28, 2026
Merged

alexr-bq merged 3 commits into
release-2from
add-forced-iam-token-refresh-api-to-the-ja-e7facdbe

Conversation

@alexr-bq

Copy link
Copy Markdown
Collaborator

Summary

  • Adds Java ElastiCacheIamAuthTokenManager.refreshToken() with behavior aligned to Node’s existing API.
  • Immediately invalidates rejected cached tokens and resolves fresh AWS credentials.
  • Shares forced, foreground, and background refresh operations.
  • Resets scheduling after replacement installation.
  • Documents AUTH-rejection recovery.
  • Adds credential-rotation, concurrency, background-race, failure, close-race, and public API coverage.

Verification

  • mvn verify: 50 tests passed.
  • Manager concurrency tests passed five consecutive runs.
  • Installed-artifact API test passed.
  • Java examples compiled successfully.

@edlng edlng left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just nits

assertNotEquals(first, replacement);
assertEquals(replacement, harness.manager.getToken());
assertEquals(2, harness.credentialCalls.get());
assertEquals(java.util.Arrays.asList(first, replacement), harness.changedTokens);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import java.util.Arrays

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated

Throwable concurrentFailure = concurrent.join();
assertTrue(forcedFailure instanceof ConfigurationException);
assertSame(forcedFailure, concurrentFailure);
assertNotEquals(rejected, forcedFailure.getMessage());

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe something stronger would be an assert that tests it doesn't include it

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strengthened this to explicitly assert that the rejected token is absent from the failure message.

assertNotNull(ElastiCacheIamAuthTokenProvider.builder());
assertNotNull(ElastiCacheIamAuthTokenManager.builder());
Method refreshToken = ElastiCacheIamAuthTokenManager.class.getMethod("refreshToken");
assertTrue(refreshToken.getReturnType().equals(String.class));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertEquals(String.class, refreshToken.getReturnType()) so a failure prints the actual type. Reflection also isn't needed here: a plain call such as String s = manager.refreshToken(); checks the same thing at compile time.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed reflection. The integration test now directly calls String token = manager.refreshToken() and verifies the result.

}

/**
* Forces an immediate token refresh.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "immediate" isn't quite true if a refresh is already in retry backoff. refresh() returns the existing inFlight future, so the caller waits out the remaining backoff instead of retrying right away. We should soften the wording here and in the README (e.g. "Invalidates the cached token and refreshes without waiting for the scheduled refresh; joins a refresh already in progress, including one in retry backoff").

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the wording

* @throws TokenRefreshException if the manager is closed
* @throws ConfigurationException if usable AWS credentials cannot be resolved
*/
public String refreshToken() {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In finishFailedRefresh there could be a race: it reads the cached token under the lock but calls refresh.complete(validToken) after releasing it. If refreshToken() runs in between, it clears cached, joins this same in-flight future, and gets back the token it invalidated. Moving the complete call inside the synchronized block, like installAndComplete does, closes the gap.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. finishFailedRefresh now completes the shared future while still holding the cache lock, so forced invalidation cannot interleave and receive the rejected fallback token.

@mwaisaida-13 mwaisaida-13 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, LGTM :shipit:

@alexr-bq
alexr-bq merged commit da40494 into release-2 Sep 28, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants