Skip to content

Commit d3a8041

Browse files
committed
fixed:
Refactor Thread-Specific Storage pattern implementation to address Sonar issues: remove unnecessary instantiation, add private constructor, use diamond operator, and update tests.
1 parent 106f06d commit d3a8041

File tree

6 files changed

+32
-24
lines changed

6 files changed

+32
-24
lines changed

thread-specific-storage/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,12 @@
5959
<artifactId>mockito-core</artifactId>
6060
<scope>test</scope>
6161
</dependency>
62+
<dependency>
63+
<groupId>org.awaitility</groupId>
64+
<artifactId>awaitility</artifactId>
65+
<version>4.2.1</version>
66+
<scope>test</scope>
67+
</dependency>
6268
</dependencies>
6369

6470
<build>

thread-specific-storage/src/main/java/com/iluwatar/threadspecificstorage/App.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,6 @@ public class App {
1717
* @param args command-line arguments (not used)
1818
*/
1919
public static void main(String[] args) {
20-
// Initialize components
21-
UserContextProxy proxy = new UserContextProxy();
22-
2320
// Simulate concurrent requests from multiple users
2421
for (int i = 1; i <= 5; i++) {
2522
// Simulate tokens for different users
@@ -28,7 +25,7 @@ public static void main(String[] args) {
2825
new Thread(
2926
() -> {
3027
// Simulate request processing flow
31-
RequestHandler handler = new RequestHandler(proxy, token);
28+
RequestHandler handler = new RequestHandler(token);
3229
handler.process();
3330
})
3431
.start();
@@ -37,6 +34,7 @@ public static void main(String[] args) {
3734
try {
3835
Thread.sleep(50);
3936
} catch (InterruptedException e) {
37+
Thread.currentThread().interrupt();
4038
LOGGER.warn("Sleep interrupted", e);
4139
}
4240
}

thread-specific-storage/src/main/java/com/iluwatar/threadspecificstorage/RequestHandler.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package com.iluwatar.threadspecificstorage;
22

33
import java.security.SecureRandom;
4-
import lombok.AllArgsConstructor;
54
import lombok.extern.slf4j.Slf4j;
65

76
/**
@@ -10,12 +9,14 @@
109
* <p>Each instance simulates a request-processing thread that uses the Thread-Specific Object Proxy
1110
* to access Thread-Specific Object.
1211
*/
13-
@AllArgsConstructor
1412
@Slf4j
1513
public class RequestHandler {
16-
private final UserContextProxy contextProxy;
1714
private final String token;
1815

16+
public RequestHandler(String token) {
17+
this.token = token;
18+
}
19+
1920
/**
2021
* Simulated business process: 1. Parse userId from token ("Token::userId"). 2. Store userId in
2122
* thread-local storage. 3. Later, retrieve userId and use it for business logic. 4. Finally,
@@ -29,13 +30,13 @@ public void process() {
2930
Long userId = parseToken(token);
3031

3132
// Step 2: Save userId in ThreadLocal storage
32-
contextProxy.set(new UserContext(userId));
33+
UserContextProxy.set(new UserContext(userId));
3334

3435
// Simulate delay between stages of request handling
3536
Thread.sleep(200);
3637

3738
// Step 3: Retrieve userId later in the request flow
38-
Long retrievedId = contextProxy.get().getUserId();
39+
Long retrievedId = UserContextProxy.get().getUserId();
3940
SecureRandom random = new SecureRandom();
4041
String accountInfo = retrievedId + "'s account: " + random.nextInt(400);
4142
LOGGER.info(accountInfo);
@@ -44,7 +45,7 @@ public void process() {
4445
Thread.currentThread().interrupt();
4546
} finally {
4647
// Step 4: Clear ThreadLocal to avoid potential memory leaks
47-
contextProxy.clear();
48+
UserContextProxy.clear();
4849
}
4950
}
5051

thread-specific-storage/src/main/java/com/iluwatar/threadspecificstorage/UserContextProxy.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ public class UserContextProxy {
1111
* Underlying TSObjectCollection (ThreadLocalMap) managed by JVM.This ThreadLocal acts as the Key
1212
* for the map.So That there is also no key factory.
1313
*/
14-
private static final ThreadLocal<UserContext> userContextHolder = new ThreadLocal<UserContext>();
14+
private static final ThreadLocal<UserContext> userContextHolder = new ThreadLocal<>();
15+
16+
/** Private constructor to prevent instantiation of this utility class. */
17+
private UserContextProxy() {}
1518

1619
/** Set UserContext for the current thread. */
1720
public static void set(UserContext context) {

thread-specific-storage/src/test/java/com/iluwatar/threadspecificstorage/AppTest.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package com.iluwatar.threadspecificstorage;
22

3+
import static java.util.concurrent.TimeUnit.MILLISECONDS;
4+
import static java.util.concurrent.TimeUnit.SECONDS;
5+
import static org.awaitility.Awaitility.await;
36
import static org.junit.jupiter.api.Assertions.assertTrue;
47

58
import java.io.ByteArrayOutputStream;
@@ -18,11 +21,10 @@ void testMainMethod() {
1821
App.main(new String[] {});
1922

2023
// Give some time for threads to execute
21-
try {
22-
Thread.sleep(2000);
23-
} catch (InterruptedException e) {
24-
Thread.currentThread().interrupt();
25-
}
24+
await()
25+
.atMost(5, SECONDS)
26+
.pollInterval(100, MILLISECONDS)
27+
.until(() -> outContent.toString().contains("Start handling request with token"));
2628

2729
// Verify output contains expected log messages
2830
String output = outContent.toString();

thread-specific-storage/src/test/java/com/iluwatar/threadspecificstorage/RequestHandlerTest.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,25 @@ class RequestHandlerTest {
88

99
@Test
1010
void process_shouldStoreAndClearUserContext() {
11-
// Given - a real UserContextProxy
12-
UserContextProxy proxy = new UserContextProxy();
13-
RequestHandler handler = new RequestHandler(proxy, "token::123");
11+
// Given - a request handler without proxy parameter
12+
RequestHandler handler = new RequestHandler("token::123");
1413

1514
// When - process the request
1615
handler.process();
1716

1817
// Then - after processing, ThreadLocal should be cleared
19-
assertNull(proxy.get(), "ThreadLocal should be cleared after process()");
18+
assertNull(UserContextProxy.get(), "ThreadLocal should be cleared after process()");
2019
}
2120

2221
@Test
2322
void process_withInvalidToken_shouldSetUserIdToMinusOne() {
24-
// Given - a real UserContextProxy
25-
UserContextProxy proxy = new UserContextProxy();
26-
RequestHandler handler = new RequestHandler(proxy, "invalid-token");
23+
// Given - a request handler without proxy parameter
24+
RequestHandler handler = new RequestHandler("invalid-token");
2725

2826
// When - process the request
2927
handler.process();
3028

3129
// Then - after processing, ThreadLocal should be cleared
32-
assertNull(proxy.get(), "ThreadLocal should be cleared even for invalid token");
30+
assertNull(UserContextProxy.get(), "ThreadLocal should be cleared even for invalid token");
3331
}
3432
}

0 commit comments

Comments
 (0)