Forráskód Böngészése

LazySupplier refactoring

Jason Rivard 2 éve
szülő
commit
f3d9078bf4
24 módosított fájl, 351 hozzáadás és 142 törlés
  1. 60 0
      lib-util/src/main/java/password/pwm/util/java/FunctionalReentrantLock.java
  2. 0 55
      lib-util/src/main/java/password/pwm/util/java/LazySoftReference.java
  3. 23 48
      lib-util/src/main/java/password/pwm/util/java/LazySupplier.java
  4. 176 0
      lib-util/src/main/java/password/pwm/util/java/LazySupplierImpl.java
  5. 10 6
      lib-util/src/main/java/password/pwm/util/java/StringUtil.java
  6. 39 0
      lib-util/src/test/java/password/pwm/util/java/LazySupplierTest.java
  7. 1 1
      server/src/main/java/password/pwm/PwmEnvironment.java
  8. 1 1
      server/src/main/java/password/pwm/config/AppConfig.java
  9. 3 3
      server/src/main/java/password/pwm/config/PwmSetting.java
  10. 10 10
      server/src/main/java/password/pwm/config/PwmSettingCategory.java
  11. 11 2
      server/src/main/java/password/pwm/config/PwmSettingXml.java
  12. 1 1
      server/src/main/java/password/pwm/config/profile/PwmPasswordPolicy.java
  13. 1 1
      server/src/main/java/password/pwm/config/value/AbstractValue.java
  14. 1 1
      server/src/main/java/password/pwm/config/value/FileValue.java
  15. 1 1
      server/src/main/java/password/pwm/config/value/NamedSecretValue.java
  16. 1 1
      server/src/main/java/password/pwm/config/value/PasswordValue.java
  17. 1 1
      server/src/main/java/password/pwm/config/value/X509CertificateValue.java
  18. 2 2
      server/src/main/java/password/pwm/http/PwmHttpRequestWrapper.java
  19. 3 3
      server/src/main/java/password/pwm/http/PwmRequest.java
  20. 1 1
      server/src/main/java/password/pwm/svc/AbstractPwmService.java
  21. 2 2
      server/src/main/java/password/pwm/svc/sessiontrack/UserAgentUtils.java
  22. 1 1
      server/src/main/java/password/pwm/svc/wordlist/WordlistConfiguration.java
  23. 1 1
      server/src/test/java/password/pwm/util/localdb/LocalDBExtendedTest.java
  24. 1 0
      webapp/src/main/webapp/WEB-INF/web.xml

+ 60 - 0
lib-util/src/main/java/password/pwm/util/java/FunctionalReentrantLock.java

@@ -0,0 +1,60 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2021 The PWM Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package password.pwm.util.java;
+
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.function.Supplier;
+
+public class FunctionalReentrantLock
+{
+    private final Lock readLock = new ReentrantLock();
+
+    public FunctionalReentrantLock()
+    {
+    }
+
+    public <T> T exec( final Supplier<T> block )
+    {
+        readLock.lock();
+        try
+        {
+            return block.get();
+        }
+        finally
+        {
+            readLock.unlock();
+        }
+    }
+
+    public void exec( final Runnable block )
+    {
+        readLock.lock();
+        try
+        {
+            block.run();
+        }
+        finally
+        {
+            readLock.unlock();
+        }
+    }
+}

+ 0 - 55
lib-util/src/main/java/password/pwm/util/java/LazySoftReference.java

@@ -1,55 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2021 The PWM Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package password.pwm.util.java;
-
-import java.lang.ref.SoftReference;
-import java.util.function.Supplier;
-
-/**
- * A lazy soft reference holder.  This reference will be built lazy and held softly
- * (according to the semantics of {@link SoftReference}).  This class is not thread
- * safe, and the GC may delete the reference at any time, so the {@link Supplier}
- * given to the constructor may be executed multiple times over the lifetime of
- * the reference.
- *
- * @param <E> type of object to hold
- */
-public class LazySoftReference<E>
-{
-    private volatile SoftReference<E> reference = new SoftReference<>( null );
-    private final Supplier<E> supplier;
-
-    public LazySoftReference( final Supplier<E> supplier )
-    {
-        this.supplier = supplier;
-    }
-
-    public E get()
-    {
-        E localValue = reference.get();
-        if ( localValue == null )
-        {
-            localValue = supplier.get();
-            reference = new SoftReference<>( localValue );
-        }
-        return localValue;
-    }
-}

+ 23 - 48
lib-util/src/main/java/password/pwm/util/java/LazySupplier.java

@@ -25,72 +25,47 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
 
 
 /**
 /**
- * Supplier implementation that will cache the value.   Note this implementation
- * is NOT thread safe, it is entirely possible that the underlying {@link Supplier}
- * will be invoked multiple times.
+ * Supplier wrapper implementations.
  *
  *
  * @param <T> the type of object being supplied.
  * @param <T> the type of object being supplied.
  */
  */
-public class LazySupplier<T> implements Supplier<T>
+public interface LazySupplier<T> extends Supplier<T>
 {
 {
-    private boolean supplied = false;
-    private T value;
-    private final Supplier<T> realSupplier;
+    boolean isSupplied();
 
 
-    public LazySupplier( final Supplier<T> realSupplier )
-    {
-        this.realSupplier = realSupplier;
-    }
+    void clear() throws UnsupportedOperationException;
 
 
-    @Override
-    public T get()
+    @SuppressFBWarnings( "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION" )
+    interface CheckedSupplier<T, E extends Exception>
     {
     {
-        if ( !supplied )
-        {
-            value = realSupplier.get();
-            supplied = true;
-        }
-        return value;
+        T call() throws E;
     }
     }
 
 
-    public boolean isSupplied()
+    /**
+     * Synchronized wrapper for any other {@code LazySupplier} implementation that
+     * guarantee thread safety.  In particular, the backing realSupplier will only ever be called
+     * a single time unless {@code #clear} is invoked.
+     * @param realSupplier another {@code LazySupplier} instance
+     * @param <T> return type.
+     * @return a {@code LazyWrapper} thread safe synchronization.
+     */
+    static <T> LazySupplier<T> synchronizedSupplier( final LazySupplier<T> realSupplier )
     {
     {
-        return supplied;
+        return new LazySupplierImpl.LockingSupplier<>( realSupplier );
     }
     }
 
 
-    @SuppressFBWarnings( "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION" )
-    public interface CheckedSupplier<T, E extends Exception>
+    static <T> LazySupplier<T> create( final Supplier<T> realSupplier )
     {
     {
-        T call() throws E;
+        return new LazySupplierImpl.StandardLazySupplier<T>( realSupplier );
     }
     }
 
 
-    public static <T, E extends Exception> LazyCheckedSupplier<T, E> checked( final CheckedSupplier<T, E> lazySupplier )
+    static <T> LazySupplier<T> soft( final Supplier<T> realSupplier )
     {
     {
-        return new LazyCheckedSupplier<>( lazySupplier );
+        return new LazySupplierImpl.SoftLazySupplier<T>( realSupplier );
     }
     }
 
 
-    @SuppressFBWarnings( "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION" )
-    private static class LazyCheckedSupplier<T, E extends Exception> implements CheckedSupplier<T, E>
+    static <T, E extends Exception> CheckedSupplier<T, E> checked( final CheckedSupplier<T, E> lazySupplier )
     {
     {
-        private boolean supplied = false;
-        private T value;
-        private final CheckedSupplier<T, E> realCallable;
-
-        private LazyCheckedSupplier( final CheckedSupplier<T, E> realSupplier )
-        {
-            this.realCallable = realSupplier;
-        }
-
-        @Override
-        @SuppressFBWarnings( "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION" )
-        public T call() throws E
-        {
-            if ( !supplied )
-            {
-                value = realCallable.call();
-                supplied = true;
-            }
-            return value;
-        }
+        return new LazySupplierImpl.LazyCheckedSupplier<>( lazySupplier );
     }
     }
 }
 }

+ 176 - 0
lib-util/src/main/java/password/pwm/util/java/LazySupplierImpl.java

@@ -0,0 +1,176 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2021 The PWM Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package password.pwm.util.java;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+import java.lang.ref.SoftReference;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+class LazySupplierImpl
+{
+    static class StandardLazySupplier<T> implements LazySupplier<T>
+    {
+        private boolean supplied = false;
+        private T value;
+        private final Supplier<T> realSupplier;
+
+        StandardLazySupplier( final Supplier<T> realSupplier )
+        {
+            this.realSupplier = realSupplier;
+        }
+
+        @Override
+        public T get()
+        {
+            if ( !supplied )
+            {
+                value = realSupplier.get();
+                supplied = true;
+            }
+            return value;
+        }
+
+        public boolean isSupplied()
+        {
+            return supplied;
+        }
+
+        @Override
+        public void clear()
+                throws UnsupportedOperationException
+        {
+            supplied = false;
+            value = null;
+        }
+    }
+
+    static class SoftLazySupplier<T> implements LazySupplier<T>
+    {
+        private static final Object TOMBSTONE = new Object();
+
+        private SoftReference<?> reference;
+        private final Supplier<T> realSupplier;
+
+        SoftLazySupplier( final Supplier<T> realSupplier )
+        {
+            this.realSupplier = Objects.requireNonNull( realSupplier );
+        }
+
+        @Override
+        public T get()
+        {
+            if ( reference != null )
+            {
+                final Object referencedValue = reference.get();
+                if ( referencedValue != null )
+                {
+                    return referencedValue == TOMBSTONE ? null : ( T ) referencedValue;
+                }
+            }
+
+            final T realValue = realSupplier.get();
+            reference = new SoftReference<>( realValue == null ? TOMBSTONE : realValue );
+            return realValue;
+        }
+
+        public boolean isSupplied()
+        {
+            return reference.get() != null;
+        }
+
+        public void clear()
+        {
+            reference = null;
+        }
+    }
+
+    static class LockingSupplier<T> implements LazySupplier<T>
+    {
+        private final Supplier<T> realSupplier;
+        private volatile T value;
+        private volatile boolean supplied = false;
+
+        private final FunctionalReentrantLock lock = new FunctionalReentrantLock();
+
+        LockingSupplier( final Supplier<T> realSupplier )
+        {
+            this.realSupplier = Objects.requireNonNull( realSupplier );
+        }
+
+        @Override
+        public T get()
+        {
+            return lock.exec( () ->
+            {
+                if ( !supplied )
+                {
+                    value = realSupplier.get();
+                    supplied = true;
+                }
+
+                return value;
+            } );
+        }
+
+        @Override
+        public boolean isSupplied()
+        {
+            return lock.exec( () -> supplied );
+        }
+
+        @Override
+        public void clear()
+        {
+            lock.exec( () ->
+            {
+                supplied = false;
+                value = null;
+            } );
+        }
+    }
+
+    @SuppressFBWarnings( "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION" )
+    static class LazyCheckedSupplier<T, E extends Exception> implements LazySupplier.CheckedSupplier<T, E>
+    {
+        private boolean supplied = false;
+        private T value;
+        private final LazySupplier.CheckedSupplier<T, E> realSupplier;
+
+        LazyCheckedSupplier( final LazySupplier.CheckedSupplier<T, E> realSupplier )
+        {
+            this.realSupplier = Objects.requireNonNull( realSupplier );
+        }
+
+        @Override
+        @SuppressFBWarnings( "THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION" )
+        public T call() throws E
+        {
+            if ( !supplied )
+            {
+                value = realSupplier.call();
+                supplied = true;
+            }
+            return value;
+        }
+    }
+}

+ 10 - 6
lib-util/src/main/java/password/pwm/util/java/StringUtil.java

@@ -768,24 +768,28 @@ public abstract class StringUtil
      */
      */
     public static boolean convertStrToBoolean( final String string )
     public static boolean convertStrToBoolean( final String string )
     {
     {
-        return !( string == null || string.length() < 1 ) && ( "true".equalsIgnoreCase( string )
+        if ( StringUtil.isEmpty( string ) )
+        {
+            return false;
+        }
+
+        return "true".equalsIgnoreCase( string )
                 || "1".equalsIgnoreCase( string )
                 || "1".equalsIgnoreCase( string )
                 || "yes".equalsIgnoreCase( string )
                 || "yes".equalsIgnoreCase( string )
-                || "y".equalsIgnoreCase( string )
-        );
+                || "y".equalsIgnoreCase( string );
     }
     }
 
 
     public static List<String> tokenizeString(
     public static List<String> tokenizeString(
             final String inputString,
             final String inputString,
-            final String seperator
+            final String separator
     )
     )
     {
     {
-        if ( inputString == null || inputString.length() < 1 )
+        if ( StringUtil.isEmpty( inputString ) )
         {
         {
             return Collections.emptyList();
             return Collections.emptyList();
         }
         }
 
 
-        final List<String> values = new ArrayList<>( Arrays.asList( inputString.split( seperator ) ) );
+        final List<String> values = new ArrayList<>( Arrays.asList( inputString.split( separator ) ) );
         return Collections.unmodifiableList( values );
         return Collections.unmodifiableList( values );
     }
     }
 }
 }

+ 39 - 0
lib-util/src/test/java/password/pwm/util/java/LazySupplierTest.java

@@ -0,0 +1,39 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2021 The PWM Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package password.pwm.util.java;
+
+import junit.framework.TestCase;
+import org.junit.Assert;
+
+public class LazySupplierTest extends TestCase
+{
+    public void testCreate()
+    {
+        final LazySupplier<String> supplier = LazySupplier.create( () -> "test1" );
+
+        Assert.assertFalse( supplier.isSupplied() );
+        Assert.assertEquals( "test1", supplier.get() );
+        Assert.assertTrue( supplier.isSupplied() );
+        supplier.clear();
+        Assert.assertFalse( supplier.isSupplied() );
+        Assert.assertEquals( "test1", supplier.get() );
+    }
+}

+ 1 - 1
server/src/main/java/password/pwm/PwmEnvironment.java

@@ -72,7 +72,7 @@ public class PwmEnvironment
     @Singular
     @Singular
     private Map<ApplicationParameter, String> parameters;
     private Map<ApplicationParameter, String> parameters;
 
 
-    private final LazySupplier<DeploymentPlatform> deploymentPlatformLazySupplier = new LazySupplier<>( this::determineDeploymentPlatform );
+    private final LazySupplier<DeploymentPlatform> deploymentPlatformLazySupplier = LazySupplier.create( this::determineDeploymentPlatform );
 
 
     public enum ApplicationParameter
     public enum ApplicationParameter
     {
     {

+ 1 - 1
server/src/main/java/password/pwm/config/AppConfig.java

@@ -78,7 +78,7 @@ public class AppConfig implements SettingReader
     private final Map<AppProperty, String> appPropertyOverrides;
     private final Map<AppProperty, String> appPropertyOverrides;
     private final Map<Locale, String> localeFlagMap;
     private final Map<Locale, String> localeFlagMap;
 
 
-    private static final Supplier<AppConfig> DEFAULT_CONFIG = new LazySupplier<>( AppConfig::makeDefaultConfig );
+    private static final Supplier<AppConfig> DEFAULT_CONFIG = LazySupplier.create( AppConfig::makeDefaultConfig );
 
 
     private static AppConfig makeDefaultConfig()
     private static AppConfig makeDefaultConfig()
     {
     {

+ 3 - 3
server/src/main/java/password/pwm/config/PwmSetting.java

@@ -1337,13 +1337,13 @@ public enum PwmSetting
 
 
     private static final Map<PwmSetting, List<TemplateSetReference<StoredValue>>> DEFAULT_VALUE_CACHE = initDefaultValueCache();
     private static final Map<PwmSetting, List<TemplateSetReference<StoredValue>>> DEFAULT_VALUE_CACHE = initDefaultValueCache();
 
 
-    private final transient Supplier<String> defaultMenuLocation = new LazySupplier<>(
+    private final transient Supplier<String> defaultMenuLocation = LazySupplier.create(
             () -> readMenuLocationDebug( this, null, PwmConstants.DEFAULT_LOCALE ) );
             () -> readMenuLocationDebug( this, null, PwmConstants.DEFAULT_LOCALE ) );
 
 
-    private final transient Supplier<String> defaultLocaleLabel = new LazySupplier<>(
+    private final transient Supplier<String> defaultLocaleLabel = LazySupplier.create(
             () -> readLabel( this, PwmConstants.DEFAULT_LOCALE ) );
             () -> readLabel( this, PwmConstants.DEFAULT_LOCALE ) );
 
 
-    private final transient Supplier<String> defaultLocaleDescription = new LazySupplier<>(
+    private final transient Supplier<String> defaultLocaleDescription = LazySupplier.create(
             () -> readDescription( this, PwmConstants.DEFAULT_LOCALE ) );
             () -> readDescription( this, PwmConstants.DEFAULT_LOCALE ) );
 
 
 
 

+ 10 - 10
server/src/main/java/password/pwm/config/PwmSettingCategory.java

@@ -212,21 +212,21 @@ public enum PwmSettingCategory
     private static final Comparator<PwmSettingCategory> MENU_LOCATION_COMPARATOR = Comparator.comparing(
     private static final Comparator<PwmSettingCategory> MENU_LOCATION_COMPARATOR = Comparator.comparing(
             ( pwmSettingCategory ) -> pwmSettingCategory.toMenuLocationDebug( null, PwmConstants.DEFAULT_LOCALE ) );
             ( pwmSettingCategory ) -> pwmSettingCategory.toMenuLocationDebug( null, PwmConstants.DEFAULT_LOCALE ) );
 
 
-    private static final Supplier<List<PwmSettingCategory>> SORTED_VALUES = new LazySupplier<>( () -> Collections.unmodifiableList( Arrays.stream( values() )
+    private static final Supplier<List<PwmSettingCategory>> SORTED_VALUES = LazySupplier.create( () -> Collections.unmodifiableList( Arrays.stream( values() )
             .sorted( MENU_LOCATION_COMPARATOR )
             .sorted( MENU_LOCATION_COMPARATOR )
             .collect( Collectors.toList() ) ) );
             .collect( Collectors.toList() ) ) );
 
 
     private final PwmSettingCategory parent;
     private final PwmSettingCategory parent;
 
 
-    private final transient Supplier<Optional<PwmSetting>> profileSetting = new LazySupplier<>( () -> DataReader.readProfileSettingFromXml( this, true ) );
-    private final transient Supplier<Integer> level = new LazySupplier<>( () -> DataReader.readLevel( this ) );
-    private final transient Supplier<Boolean> hidden = new LazySupplier<>( () -> DataReader.readHidden( this ) );
-    private final transient Supplier<Boolean> isTopLevelProfile = new LazySupplier<>( () -> DataReader.readIsTopLevelProfile( this ) );
-    private final transient Supplier<String> defaultLocaleLabel = new LazySupplier<>( () -> DataReader.readLabel( this, PwmConstants.DEFAULT_LOCALE ) );
-    private final transient Supplier<String> defaultLocaleDescription = new LazySupplier<>( () -> DataReader.readDescription( this, PwmConstants.DEFAULT_LOCALE ) );
-    private final transient Supplier<PwmSettingScope> scope = new LazySupplier<>( () -> DataReader.readScope( this ) );
-    private final transient Supplier<Set<PwmSettingCategory>> children = new LazySupplier<>( () -> DataReader.readChildren( this ) );
-    private final transient Supplier<Set<PwmSetting>> settings = new LazySupplier<>( () -> DataReader.readSettings( this ) );
+    private final transient Supplier<Optional<PwmSetting>> profileSetting = LazySupplier.create( () -> DataReader.readProfileSettingFromXml( this, true ) );
+    private final transient Supplier<Integer> level = LazySupplier.create( () -> DataReader.readLevel( this ) );
+    private final transient Supplier<Boolean> hidden = LazySupplier.create( () -> DataReader.readHidden( this ) );
+    private final transient Supplier<Boolean> isTopLevelProfile = LazySupplier.create( () -> DataReader.readIsTopLevelProfile( this ) );
+    private final transient Supplier<String> defaultLocaleLabel = LazySupplier.create( () -> DataReader.readLabel( this, PwmConstants.DEFAULT_LOCALE ) );
+    private final transient Supplier<String> defaultLocaleDescription = LazySupplier.create( () -> DataReader.readDescription( this, PwmConstants.DEFAULT_LOCALE ) );
+    private final transient Supplier<PwmSettingScope> scope = LazySupplier.create( () -> DataReader.readScope( this ) );
+    private final transient Supplier<Set<PwmSettingCategory>> children = LazySupplier.create( () -> DataReader.readChildren( this ) );
+    private final transient Supplier<Set<PwmSetting>> settings = LazySupplier.create( () -> DataReader.readSettings( this ) );
 
 
     PwmSettingCategory( final PwmSettingCategory parent )
     PwmSettingCategory( final PwmSettingCategory parent )
     {
     {

+ 11 - 2
server/src/main/java/password/pwm/config/PwmSettingXml.java

@@ -25,7 +25,7 @@ import org.jrivard.xmlchai.XmlChai;
 import org.jrivard.xmlchai.XmlDocument;
 import org.jrivard.xmlchai.XmlDocument;
 import org.jrivard.xmlchai.XmlElement;
 import org.jrivard.xmlchai.XmlElement;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
-import password.pwm.util.java.LazySoftReference;
+import password.pwm.util.java.LazySupplier;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
@@ -36,6 +36,9 @@ import java.util.Collections;
 import java.util.LinkedHashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.List;
 import java.util.Set;
 import java.util.Set;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicInteger;
 
 
 public class PwmSettingXml
 public class PwmSettingXml
@@ -69,7 +72,9 @@ public class PwmSettingXml
 
 
     private static final PwmLogger LOGGER = PwmLogger.forClass( PwmSettingXml.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( PwmSettingXml.class );
 
 
-    private static final LazySoftReference<XmlDocument> XML_DOC_CACHE = new LazySoftReference<>( PwmSettingXml::readXml );
+    private static final LazySupplier<XmlDocument> XML_DOC_CACHE = LazySupplier.synchronizedSupplier(
+            LazySupplier.create( PwmSettingXml::readXml ) );
+
     private static final AtomicInteger LOAD_COUNTER = new AtomicInteger( 0 );
     private static final AtomicInteger LOAD_COUNTER = new AtomicInteger( 0 );
 
 
     private static XmlDocument readXml( )
     private static XmlDocument readXml( )
@@ -80,6 +85,10 @@ public class PwmSettingXml
             final XmlDocument newDoc = XmlChai.getFactory().parse( inputStream, AccessMode.IMMUTABLE );
             final XmlDocument newDoc = XmlChai.getFactory().parse( inputStream, AccessMode.IMMUTABLE );
             final TimeDuration parseDuration = TimeDuration.fromCurrent( startTime );
             final TimeDuration parseDuration = TimeDuration.fromCurrent( startTime );
             LOGGER.trace( () -> "parsed PwmSettingXml in " + parseDuration.asCompactString() + ", loads=" + LOAD_COUNTER.getAndIncrement() );
             LOGGER.trace( () -> "parsed PwmSettingXml in " + parseDuration.asCompactString() + ", loads=" + LOAD_COUNTER.getAndIncrement() );
+
+            final ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
+            scheduledExecutorService.schedule( XML_DOC_CACHE::clear, 30, TimeUnit.SECONDS );
+
             return newDoc;
             return newDoc;
         }
         }
         catch ( final IOException e )
         catch ( final IOException e )

+ 1 - 1
server/src/main/java/password/pwm/config/profile/PwmPasswordPolicy.java

@@ -68,7 +68,7 @@ public class PwmPasswordPolicy implements Profile, Serializable
 
 
     private static final PwmPasswordPolicy DEFAULT_POLICY = makeDefaultPolicy();
     private static final PwmPasswordPolicy DEFAULT_POLICY = makeDefaultPolicy();
 
 
-    private final transient Supplier<List<HealthRecord>> healthChecker = new LazySupplier<>( () -> doHealthChecks( this ) );
+    private final transient Supplier<List<HealthRecord>> healthChecker = LazySupplier.create( () -> doHealthChecks( this ) );
     private final transient ChaiPasswordPolicy chaiPasswordPolicy;
     private final transient ChaiPasswordPolicy chaiPasswordPolicy;
 
 
     private final Map<String, String> policyMap;
     private final Map<String, String> policyMap;

+ 1 - 1
server/src/main/java/password/pwm/config/value/AbstractValue.java

@@ -59,7 +59,7 @@ public abstract class AbstractValue implements StoredValue
         }
         }
     }
     }
 
 
-    private final transient LazySupplier<String> valueHashSupplier = new LazySupplier<>( () -> valueHashComputer( AbstractValue.this ) );
+    private final transient LazySupplier<String> valueHashSupplier = LazySupplier.create( () -> valueHashComputer( AbstractValue.this ) );
 
 
     public String toString()
     public String toString()
     {
     {

+ 1 - 1
server/src/main/java/password/pwm/config/value/FileValue.java

@@ -80,7 +80,7 @@ public class FileValue extends AbstractValue implements StoredValue
         private FileContent( final String b64EncodedContents )
         private FileContent( final String b64EncodedContents )
         {
         {
             this.b64EncodedContents = b64EncodedContents;
             this.b64EncodedContents = b64EncodedContents;
-            this.byteContents = new LazySupplier<>( () -> b64decode( b64EncodedContents ) );
+            this.byteContents = LazySupplier.create( () -> b64decode( b64EncodedContents ) );
         }
         }
 
 
         public static FileContent fromEncodedString( final String input )
         public static FileContent fromEncodedString( final String input )

+ 1 - 1
server/src/main/java/password/pwm/config/value/NamedSecretValue.java

@@ -53,7 +53,7 @@ public class NamedSecretValue implements StoredValue
 {
 {
     private static final long serialVersionUID = 1L;
     private static final long serialVersionUID = 1L;
 
 
-    private final transient LazySupplier<String> valueHashSupplier = new LazySupplier<>( () -> AbstractValue.valueHashComputer( NamedSecretValue.this ) );
+    private final transient LazySupplier<String> valueHashSupplier = LazySupplier.create( () -> AbstractValue.valueHashComputer( NamedSecretValue.this ) );
 
 
     private static final String ELEMENT_NAME = "name";
     private static final String ELEMENT_NAME = "name";
     private static final String ELEMENT_PASSWORD = "password";
     private static final String ELEMENT_PASSWORD = "password";

+ 1 - 1
server/src/main/java/password/pwm/config/value/PasswordValue.java

@@ -47,7 +47,7 @@ public class PasswordValue implements StoredValue
 {
 {
     private static final long serialVersionUID = 1L;
     private static final long serialVersionUID = 1L;
 
 
-    private final transient LazySupplier<String> valueHashSupplier = new LazySupplier<>( () -> AbstractValue.valueHashComputer( PasswordValue.this ) );
+    private final transient LazySupplier<String> valueHashSupplier = LazySupplier.create( () -> AbstractValue.valueHashComputer( PasswordValue.this ) );
 
 
     private final PasswordData value;
     private final PasswordData value;
 
 

+ 1 - 1
server/src/main/java/password/pwm/config/value/X509CertificateValue.java

@@ -95,7 +95,7 @@ public class X509CertificateValue extends AbstractValue implements StoredValue
         this.b64certificates = b64certificates.stream()
         this.b64certificates = b64certificates.stream()
                 .map( StringUtil::stripAllWhitespace )
                 .map( StringUtil::stripAllWhitespace )
                 .collect( Collectors.toUnmodifiableList() );
                 .collect( Collectors.toUnmodifiableList() );
-        this.certs = new LazySupplier<>( () -> X509Utils.certificatesFromBase64s( b64certificates ) );
+        this.certs = LazySupplier.create( () -> X509Utils.certificatesFromBase64s( b64certificates ) );
     }
     }
 
 
     public static X509CertificateValue fromX509( final Collection<X509Certificate> x509Certificate )
     public static X509CertificateValue fromX509( final Collection<X509Certificate> x509Certificate )

+ 2 - 2
server/src/main/java/password/pwm/http/PwmHttpRequestWrapper.java

@@ -69,10 +69,10 @@ public class PwmHttpRequestWrapper
     private static final Set<String> HTTP_HEADER_DEBUG_STRIP_VALUES = Set.of(
     private static final Set<String> HTTP_HEADER_DEBUG_STRIP_VALUES = Set.of(
                     HttpHeader.Authorization.getHttpName() );
                     HttpHeader.Authorization.getHttpName() );
 
 
-    private final Supplier<Optional<String>> srcHostnameSupplier = new LazySupplier<>(
+    private final Supplier<Optional<String>> srcHostnameSupplier = LazySupplier.create(
             () -> PwmRequestUtil.readUserHostname( this.getHttpServletRequest(), this.getAppConfig() ) );
             () -> PwmRequestUtil.readUserHostname( this.getHttpServletRequest(), this.getAppConfig() ) );
 
 
-    private final Supplier<Optional<String>> srcAddressSupplier = new LazySupplier<>(
+    private final Supplier<Optional<String>> srcAddressSupplier = LazySupplier.create(
             () -> PwmRequestUtil.readUserNetworkAddress( this.getHttpServletRequest(), this.getAppConfig() ) );
             () -> PwmRequestUtil.readUserNetworkAddress( this.getHttpServletRequest(), this.getAppConfig() ) );
 
 
     public enum Flag
     public enum Flag

+ 3 - 3
server/src/main/java/password/pwm/http/PwmRequest.java

@@ -99,13 +99,13 @@ public class PwmRequest extends PwmHttpRequestWrapper
     private final DomainID domainID;
     private final DomainID domainID;
     private final Lock cspCreationLock = new ReentrantLock();
     private final Lock cspCreationLock = new ReentrantLock();
 
 
-    private final Supplier<Locale> localeSupplier = new LazySupplier<>(
+    private final Supplier<Locale> localeSupplier = LazySupplier.create(
             () -> PwmRequestLocaleResolver.resolveRequestLocale( this ) );
             () -> PwmRequestLocaleResolver.resolveRequestLocale( this ) );
 
 
-    private final Supplier<PwmRequestContext> requestContextSupplier = new LazySupplier<>(
+    private final Supplier<PwmRequestContext> requestContextSupplier = LazySupplier.create(
             this::makePwmRequestContext );
             this::makePwmRequestContext );
 
 
-    private final Supplier<SessionLabel> sessionLabelSupplier = new LazySupplier<>(
+    private final Supplier<SessionLabel> sessionLabelSupplier = LazySupplier.create(
             () -> PwmRequestUtil.makeSessionLabel( this ) );
             () -> PwmRequestUtil.makeSessionLabel( this ) );
 
 
     public static PwmRequest forRequest(
     public static PwmRequest forRequest(

+ 1 - 1
server/src/main/java/password/pwm/svc/AbstractPwmService.java

@@ -72,7 +72,7 @@ public abstract class AbstractPwmService implements PwmService
                 ? pwmApplication.getSessionLabel()
                 ? pwmApplication.getSessionLabel()
                 : pwmApplication.domains().get( domainID ).getSessionLabel();
                 : pwmApplication.domains().get( domainID ).getSessionLabel();
 
 
-        executorService = new LazySupplier<>( () -> PwmScheduler.makeBackgroundServiceExecutor( pwmApplication, getSessionLabel(), getClass() ) );
+        executorService = LazySupplier.create( () -> PwmScheduler.makeBackgroundServiceExecutor( pwmApplication, getSessionLabel(), getClass() ) );
 
 
         if ( pwmApplication.checkConditions( openConditions() ) )
         if ( pwmApplication.checkConditions( openConditions() ) )
         {
         {

+ 2 - 2
server/src/main/java/password/pwm/svc/sessiontrack/UserAgentUtils.java

@@ -32,7 +32,7 @@ import password.pwm.http.HttpHeader;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
-import password.pwm.util.java.LazySoftReference;
+import password.pwm.util.java.LazySupplier;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
@@ -46,7 +46,7 @@ public class UserAgentUtils
 {
 {
     private static final PwmLogger LOGGER = PwmLogger.forClass( UserAgentUtils.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( UserAgentUtils.class );
 
 
-    private static final LazySoftReference<UserAgentParser> CACHED_PARSER = new LazySoftReference<>( UserAgentUtils::loadUserAgentParser );
+    private static final LazySupplier<UserAgentParser> CACHED_PARSER = LazySupplier.create( UserAgentUtils::loadUserAgentParser );
 
 
     public enum BrowserType
     public enum BrowserType
     {
     {

+ 1 - 1
server/src/main/java/password/pwm/svc/wordlist/WordlistConfiguration.java

@@ -152,7 +152,7 @@ public class WordlistConfiguration implements Serializable
     }
     }
 
 
     @Getter( AccessLevel.PRIVATE )
     @Getter( AccessLevel.PRIVATE )
-    private final transient Supplier<String> configHash = new LazySupplier<>( () ->
+    private final transient Supplier<String> configHash = LazySupplier.create( () ->
             SecureEngine.hash( JsonFactory.get().serialize( WordlistConfiguration.this ), HASH_ALGORITHM ) );
             SecureEngine.hash( JsonFactory.get().serialize( WordlistConfiguration.this ), HASH_ALGORITHM ) );
 
 
     public boolean isAutoImportUrlConfigured()
     public boolean isAutoImportUrlConfigured()

+ 1 - 1
server/src/test/java/password/pwm/util/localdb/LocalDBExtendedTest.java

@@ -53,7 +53,7 @@ public class LocalDBExtendedTest
     {
     {
         Assert.assertNull( localDB.get( TEST_DB, "testKey1" ) );
         Assert.assertNull( localDB.get( TEST_DB, "testKey1" ) );
         localDB.put( TEST_DB, "testKey1", "testValue1" );
         localDB.put( TEST_DB, "testKey1", "testValue1" );
-        Assert.assertEquals( "testValue1", localDB.get( TEST_DB, "testKey1" ) );
+        Assert.assertEquals( "testValue1", localDB.get( TEST_DB, "testKey1" ).orElseThrow() );
     }
     }
 
 
     @Test
     @Test

+ 1 - 0
webapp/src/main/webapp/WEB-INF/web.xml

@@ -225,6 +225,7 @@
         <cookie-config>
         <cookie-config>
             <http-only>true</http-only>
             <http-only>true</http-only>
         </cookie-config>
         </cookie-config>
+        <tracking-mode>COOKIE</tracking-mode>
     </session-config>
     </session-config>
     <jsp-config>
     <jsp-config>
         <taglib>
         <taglib>