mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-10-04 06:13:28 +00:00
feat: Add Thread-Specific Storage design pattern (#3422)
* Add Thread-Specific Storage design pattern * fixed:pom.xml Run 'mvn spotless:apply' to fix these violations. * fix: address Sonar security hotspots for Random usage and e.printStackTrace() * fixed: Run 'mvn spotless:apply'(remove unused import) * fixed: Refactor Thread-Specific Storage pattern implementation to address Sonar issues: remove unnecessary instantiation, add private constructor, use diamond operator, and update tests. * fixed: update uml and README.md * Trigger CI rerun
This commit is contained in:
1 parent
48ba55a8a6
commit
e8595393d6
13 files changed
+702
No files matched your search
@@ -0,0 +1,38 @@
|
||||
package com.iluwatar.threadspecificstorage;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.awaitility.Awaitility.await;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for APP class */
|
||||
class AppTest {
|
||||
@Test
|
||||
void testMainMethod() {
|
||||
// Capture system output
|
||||
ByteArrayOutputStream outContent = new ByteArrayOutputStream();
|
||||
System.setOut(new PrintStream(outContent));
|
||||
|
||||
// Run the main method
|
||||
App.main(new String[] {});
|
||||
|
||||
// Give some time for threads to execute
|
||||
await()
|
||||
.atMost(5, SECONDS)
|
||||
.pollInterval(100, MILLISECONDS)
|
||||
.until(() -> outContent.toString().contains("Start handling request with token"));
|
||||
|
||||
// Verify output contains expected log messages
|
||||
String output = outContent.toString();
|
||||
assertTrue(
|
||||
output.contains("Start handling request with token"),
|
||||
"Should contain request handling start messages");
|
||||
|
||||
// Restore system output
|
||||
System.setOut(System.out);
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
package com.iluwatar.threadspecificstorage;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class RequestHandlerTest {
|
||||
|
||||
@Test
|
||||
void process_shouldStoreAndClearUserContext() {
|
||||
// Given - a request handler without proxy parameter
|
||||
RequestHandler handler = new RequestHandler("token::123");
|
||||
|
||||
// When - process the request
|
||||
handler.process();
|
||||
|
||||
// Then - after processing, ThreadLocal should be cleared
|
||||
assertNull(UserContextProxy.get(), "ThreadLocal should be cleared after process()");
|
||||
}
|
||||
|
||||
@Test
|
||||
void process_withInvalidToken_shouldSetUserIdToMinusOne() {
|
||||
// Given - a request handler without proxy parameter
|
||||
RequestHandler handler = new RequestHandler("invalid-token");
|
||||
|
||||
// When - process the request
|
||||
handler.process();
|
||||
|
||||
// Then - after processing, ThreadLocal should be cleared
|
||||
assertNull(UserContextProxy.get(), "ThreadLocal should be cleared even for invalid token");
|
||||
}
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
package com.iluwatar.threadspecificstorage;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for UserContextProxy class */
|
||||
class UserContextProxyTest {
|
||||
|
||||
private UserContext userContext;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
userContext = new UserContext(123L);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
UserContextProxy.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetAndGetContext() {
|
||||
UserContextProxy.set(userContext);
|
||||
UserContext retrievedContext = UserContextProxy.get();
|
||||
assertNotNull(retrievedContext, "Retrieved context should not be null");
|
||||
assertEquals(
|
||||
userContext.getUserId(),
|
||||
retrievedContext.getUserId(),
|
||||
"Retrieved context should have the same userId");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetContextWhenNotSet() {
|
||||
UserContext retrievedContext = UserContextProxy.get();
|
||||
assertNull(retrievedContext, "Context should be null when not set");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClearContext() {
|
||||
UserContextProxy.set(userContext);
|
||||
UserContextProxy.clear();
|
||||
UserContext retrievedContext = UserContextProxy.get();
|
||||
assertNull(retrievedContext, "Context should be null after clearing");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testThreadIsolation() throws InterruptedException {
|
||||
UserContext context1 = new UserContext(123L);
|
||||
UserContext context2 = new UserContext(456L);
|
||||
UserContextProxy.set(context1);
|
||||
// Create another thread to set different context
|
||||
Thread thread =
|
||||
new Thread(
|
||||
() -> {
|
||||
UserContextProxy.set(context2);
|
||||
UserContext threadContext = UserContextProxy.get();
|
||||
assertNotNull(threadContext);
|
||||
assertEquals(456L, threadContext.getUserId());
|
||||
});
|
||||
thread.start();
|
||||
thread.join();
|
||||
// Main thread context should remain unchanged
|
||||
UserContext mainThreadContext = UserContextProxy.get();
|
||||
assertNotNull(mainThreadContext);
|
||||
assertEquals(123L, mainThreadContext.getUserId());
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
package com.iluwatar.threadspecificstorage;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests for UserContext class */
|
||||
class UserContextTest {
|
||||
|
||||
@Test
|
||||
void testConstructorAndGetUserId() {
|
||||
Long userId = 123L;
|
||||
UserContext context = new UserContext(userId);
|
||||
|
||||
assertEquals(
|
||||
userId, context.getUserId(), "UserId should match the one provided in constructor");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSetUserId() {
|
||||
UserContext context = new UserContext(123L);
|
||||
Long newUserId = 456L;
|
||||
|
||||
context.setUserId(newUserId);
|
||||
|
||||
assertEquals(newUserId, context.getUserId(), "UserId should be updated");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testToString() {
|
||||
Long userId = 123L;
|
||||
UserContext context = new UserContext(userId);
|
||||
|
||||
String expected = "UserContext(userId=" + userId + ")";
|
||||
assertEquals(expected, context.toString(), "toString should return expected format");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testEqualsAndHashCode() {
|
||||
Long userId = 123L;
|
||||
UserContext context1 = new UserContext(userId);
|
||||
UserContext context2 = new UserContext(userId);
|
||||
UserContext context3 = new UserContext(456L);
|
||||
|
||||
assertEquals(context1, context2, "Objects with same userId should be equal");
|
||||
assertEquals(
|
||||
context1.hashCode(),
|
||||
context2.hashCode(),
|
||||
"Objects with same userId should have same hashCode");
|
||||
|
||||
assertNotEquals(context1, context3, "Objects with different userId should not be equal");
|
||||
assertNotEquals(
|
||||
context1.hashCode(),
|
||||
context3.hashCode(),
|
||||
"Objects with different userId should have different hashCode");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user