From 314f2738e2ce7e5f2210e0b51b3f36f20363af8d Mon Sep 17 00:00:00 2001 From: smile-ab <202159894+smile-ab@users.noreply.github.com> Date: Mon, 10 Mar 2025 14:39:15 +0100 Subject: [PATCH 1/2] fix typo --- .../iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java index e9d8ef0482d6..1338000a3988 100644 --- a/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java +++ b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java @@ -43,10 +43,10 @@ public ThreadSafeDoubleCheckLockingTest() { } /** - * Test creating new instance by refection. + * Test creating new instance by reflection. */ @Test - void testCreatingNewInstanceByRefection() throws Exception { + void testCreatingNewInstanceByReflection() throws Exception { ThreadSafeDoubleCheckLocking.getInstance(); var constructor = ThreadSafeDoubleCheckLocking.class.getDeclaredConstructor(); constructor.setAccessible(true); From 28bb7da5c8f879ad9f667cc1ad72c5d3611c45e2 Mon Sep 17 00:00:00 2001 From: smile-ab <202159894+smile-ab@users.noreply.github.com> Date: Mon, 10 Mar 2025 15:36:08 +0100 Subject: [PATCH 2/2] adding Reflection handling to other Singleton implementations --- .../singleton/BillPughImplementation.java | 5 ++++- .../InitializingOnDemandHolderIdiom.java | 4 ++++ .../java/com/iluwatar/singleton/IvoryTower.java | 4 ++++ .../iluwatar/singleton/EnumIvoryTowerTest.java | 14 ++++++++++++++ .../com/iluwatar/singleton/SingletonTest.java | 12 ++++++++++++ .../ThreadSafeDoubleCheckLockingTest.java | 16 ---------------- 6 files changed, 38 insertions(+), 17 deletions(-) diff --git a/singleton/src/main/java/com/iluwatar/singleton/BillPughImplementation.java b/singleton/src/main/java/com/iluwatar/singleton/BillPughImplementation.java index 4656cb7624f3..c5f6f9f2cc60 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/BillPughImplementation.java +++ b/singleton/src/main/java/com/iluwatar/singleton/BillPughImplementation.java @@ -40,7 +40,10 @@ public final class BillPughImplementation { * Private constructor to prevent instantiation from outside the class. */ private BillPughImplementation() { - // private constructor + // to prevent instantiating by Reflection call + if (InstanceHolder.instance != null) { + throw new IllegalStateException("Already initialized."); + } } /** diff --git a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java index 6cff5b561a92..f2e4277750e9 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java +++ b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java @@ -43,6 +43,10 @@ public final class InitializingOnDemandHolderIdiom { * Private constructor. */ private InitializingOnDemandHolderIdiom() { + // to prevent instantiating by Reflection call + if (HelperHolder.INSTANCE != null) { + throw new IllegalStateException("Already initialized."); + } } /** diff --git a/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java index f27467272762..362e1634cafd 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/IvoryTower.java @@ -33,6 +33,10 @@ public final class IvoryTower { * Private constructor so nobody can instantiate the class. */ private IvoryTower() { + // to prevent instantiating by Reflection call + if (INSTANCE != null) { + throw new IllegalStateException("Already initialized."); + } } /** diff --git a/singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java b/singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java index 6b1c6e235294..cd40f2d3a2ba 100644 --- a/singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java +++ b/singleton/src/test/java/com/iluwatar/singleton/EnumIvoryTowerTest.java @@ -24,6 +24,10 @@ */ package com.iluwatar.singleton; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertThrows; + /** * EnumIvoryTowerTest * @@ -37,4 +41,14 @@ public EnumIvoryTowerTest() { super(() -> EnumIvoryTower.INSTANCE); } + /** + * Test creating new instance by reflection. + */ + @Override + @Test + void testCreatingNewInstanceByReflection() throws Exception { + // Java does not allow Enum instantiation http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.9 + assertThrows(ReflectiveOperationException.class, EnumIvoryTower.class::getDeclaredConstructor); + } + } diff --git a/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java b/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java index 7093ffc7307f..81e5f4132040 100644 --- a/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java +++ b/singleton/src/test/java/com/iluwatar/singleton/SingletonTest.java @@ -27,8 +27,10 @@ import static java.time.Duration.ofMillis; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTimeout; +import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.concurrent.Callable; import java.util.concurrent.Executors; @@ -106,4 +108,14 @@ void testMultipleCallsReturnTheSameObjectInDifferentThreads() { } + /** + * Test creating new instance by reflection. + */ + @Test + void testCreatingNewInstanceByReflection() throws Exception { + var firstTimeInstantiated = this.singletonInstanceMethod.get(); + var constructor = firstTimeInstantiated.getClass().getDeclaredConstructor(); + constructor.setAccessible(true); + assertThrows(InvocationTargetException.class, () -> constructor.newInstance((Object[]) null)); + } } diff --git a/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java index 1338000a3988..e88968223cde 100644 --- a/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java +++ b/singleton/src/test/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLockingTest.java @@ -24,11 +24,6 @@ */ package com.iluwatar.singleton; -import static org.junit.jupiter.api.Assertions.assertThrows; - -import java.lang.reflect.InvocationTargetException; -import org.junit.jupiter.api.Test; - /** * ThreadSafeDoubleCheckLockingTest * @@ -42,15 +37,4 @@ public ThreadSafeDoubleCheckLockingTest() { super(ThreadSafeDoubleCheckLocking::getInstance); } - /** - * Test creating new instance by reflection. - */ - @Test - void testCreatingNewInstanceByReflection() throws Exception { - ThreadSafeDoubleCheckLocking.getInstance(); - var constructor = ThreadSafeDoubleCheckLocking.class.getDeclaredConstructor(); - constructor.setAccessible(true); - assertThrows(InvocationTargetException.class, () -> constructor.newInstance((Object[]) null)); - } - }