Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,17 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
<source>11</source>
<target>11</target>
</configuration>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.1.0</version>
</plugin>

<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>3.1.1</version>
Expand Down Expand Up @@ -104,7 +110,7 @@
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<source>8</source>
<source>11</source>
</configuration>
<executions>
<execution>
Expand Down Expand Up @@ -160,7 +166,7 @@
</scm>

<properties>
<java.version>8</java.version>
<java.version>11</java.version>
</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,21 +65,17 @@ public TokenBearer requestToken() throws Exception {
* @throws AuthorizationServiceException if the token is invalid
*/
@Override
public TokenBearer checkAndGetToken() throws Exception {
public synchronized TokenBearer checkAndGetToken() throws Exception {
if(tokenBearer != null) {

if(lastTokenGet.isBefore(Instant.now().minus(24, ChronoUnit.HOURS))) {
System.out.println("Token more than a day old, trying to retrieve another token");
try {
this.requestToken();
this.requestToken();;
} catch (IOException | ApiCredentialsException e) {
e.printStackTrace();
throw new AuthorizationServiceException("Token invalid");
}

checkAndGetToken();
}

return tokenBearer;
} else
return this.requestToken();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
import java.lang.reflect.Field;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -116,4 +121,61 @@ public void checkAndGetToken_fetchAgain_pass() throws Exception {
lastTokenGetField.setAccessible(false);
}

@Test
public void testRaceConditionInCheckAndGetToken() throws Exception {
ApiConnectorService apiConnectorService = mock(ApiConnectorService.class);int numberOfThreads = 4;
ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);
RestV2AuthorizationService authorizationService = new RestV2AuthorizationService(
correctApiCredentials,
apiConnectorService
);

// Mock the token to be expired
TokenBearer expiredToken = TokenBearer.builder().token("EXPIRED_TOKEN").build();
Mockito.lenient().when(apiConnectorService.getToken(any())).thenReturn(expiredToken);

// Manually set the lastTokenGet to be more than 24 hours ago
Field lastTokenGetField = RestV2AuthorizationService.class.getDeclaredField("lastTokenGet");
lastTokenGetField.setAccessible(true);
lastTokenGetField.set(authorizationService, Instant.now().minus(25, ChronoUnit.HOURS));

// Use CountDownLatch to start all threads at the same time
CountDownLatch latch = new CountDownLatch(1);
AtomicInteger tokenRefreshCount = new AtomicInteger(0);

// Mock apiConnectorService to count token refresh attempts
Mockito.when(apiConnectorService.getToken(any())).thenAnswer(invocation -> {
tokenRefreshCount.incrementAndGet();
// Simulate delay in token retrieval
Thread.sleep(100);
return TokenBearer.builder().token("NEW_TOKEN_" + tokenRefreshCount.get()).build();
});

// Create tasks for concurrent execution
Runnable task = () -> {
try {
latch.await(); // Wait for the latch to be released
authorizationService.checkAndGetToken();
} catch (Exception e) {
e.printStackTrace();
}
};

// Submit tasks to the executor service
for (int i = 0; i < numberOfThreads; i++) {
executorService.submit(task);
}

// Release the latch to start all threads
latch.countDown();

// Wait for all tasks to complete
executorService.shutdown();
executorService.awaitTermination(5, TimeUnit.SECONDS);

// Check that just one thread was able to refresh the token
assertEquals(1, tokenRefreshCount.get());

lastTokenGetField.setAccessible(false);
}
}
Loading