diff --git a/src/main/java/com/password4j/Argon2Function.java b/src/main/java/com/password4j/Argon2Function.java index 40e1cee..576209b 100755 --- a/src/main/java/com/password4j/Argon2Function.java +++ b/src/main/java/com/password4j/Argon2Function.java @@ -166,6 +166,11 @@ protected static String getUID(int memory, int iterations, int parallelism, int return memory + "|" + iterations + "|" + parallelism + "|" + outputLength + "|" + type.ordinal() + "|" + version; } + static void clearInstances() + { + INSTANCES.clear(); + } + private static byte[] getInitialHashLong(byte[] initialHash, byte[] appendix) { byte[] initialHashLong = new byte[ARGON2_INITIAL_SEED_LENGTH]; diff --git a/src/main/java/com/password4j/BalloonHashingFunction.java b/src/main/java/com/password4j/BalloonHashingFunction.java index 785161e..11dc9c8 100644 --- a/src/main/java/com/password4j/BalloonHashingFunction.java +++ b/src/main/java/com/password4j/BalloonHashingFunction.java @@ -88,6 +88,11 @@ private static String getUID(String algorithm, int spaceCost, int timeCost, int return algorithm + '|' + spaceCost + '|' + timeCost + '|' + parallelism + '|' + delta; } + static void clearInstances() + { + INSTANCES.clear(); + } + protected static String toString(String algorithm, int spaceCost, int timeCost, int parallelism, int delta) { return "a=" + algorithm + ", s=" + spaceCost + ", t=" + timeCost + ", p=" + parallelism + ", d=" + delta; diff --git a/src/main/java/com/password4j/Password.java b/src/main/java/com/password4j/Password.java index cc0a508..79c7d69 100755 --- a/src/main/java/com/password4j/Password.java +++ b/src/main/java/com/password4j/Password.java @@ -42,6 +42,24 @@ private Password() // } + /** + * Shuts down all internal worker thread pools used by parallel hashing functions + * (Argon2, Balloon) and clears their cached instances. This is intended for + * environments with a lifecycle shorter than the JVM — typically servlet + * containers, where it should be invoked from {@code ServletContextListener.contextDestroyed} + * to prevent thread/classloader leaks on webapp undeploy. + *
+ * After this call, subsequent hashing requests will lazily re-create the workers. + * In a plain-JVM application it is not necessary to invoke this method: worker + * threads are daemons and are shut down automatically when the JVM exits. + * + * @since 1.8.5 + */ + public static void shutdown() + { + Utils.shutdownExecutors(); + } + /** * Starts to hash the given plain text password. *
diff --git a/src/main/java/com/password4j/Utils.java b/src/main/java/com/password4j/Utils.java
index 6be8026..e24e477 100755
--- a/src/main/java/com/password4j/Utils.java
+++ b/src/main/java/com/password4j/Utils.java
@@ -29,9 +29,12 @@
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.util.Arrays;
+import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
@@ -55,6 +58,10 @@ class Utils
private static final ThreadGroup THREAD_GROUP = new ThreadGroup("Password4j Workers");
+ private static final Set