Explorar o código

errorprone fixes and refactorings

Jason Rivard %!s(int64=4) %!d(string=hai) anos
pai
achega
0c8ec1626f
Modificáronse 44 ficheiros con 316 adicións e 269 borrados
  1. 19 8
      onejar/src/main/java/password/pwm/onejar/OnejarMain.java
  2. 16 11
      onejar/src/main/java/password/pwm/onejar/TomcatOnejarRunner.java
  3. 32 32
      server/src/main/java/password/pwm/PwmApplication.java
  4. 23 29
      server/src/main/java/password/pwm/bean/UserIdentity.java
  5. 1 44
      server/src/main/java/password/pwm/config/Configuration.java
  6. 1 1
      server/src/main/java/password/pwm/config/ConfigurationUtil.java
  7. 6 15
      server/src/main/java/password/pwm/config/profile/AbstractProfile.java
  8. 1 1
      server/src/main/java/password/pwm/config/profile/ChallengeProfile.java
  9. 1 1
      server/src/main/java/password/pwm/config/profile/LdapProfile.java
  10. 1 1
      server/src/main/java/password/pwm/config/profile/Profile.java
  11. 1 1
      server/src/main/java/password/pwm/config/profile/ProfileUtility.java
  12. 1 1
      server/src/main/java/password/pwm/config/profile/PwmPasswordPolicy.java
  13. 1 1
      server/src/main/java/password/pwm/config/stored/StoredConfigZipJsonSerializer.java
  14. 2 3
      server/src/main/java/password/pwm/config/value/FileValue.java
  15. 19 0
      server/src/main/java/password/pwm/config/value/ValueTypeConverter.java
  16. 10 9
      server/src/main/java/password/pwm/config/value/VerificationMethodValue.java
  17. 6 5
      server/src/main/java/password/pwm/http/HttpContentType.java
  18. 7 0
      server/src/main/java/password/pwm/http/bean/ImmutableByteArray.java
  19. 2 2
      server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java
  20. 2 4
      server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerLoginServlet.java
  21. 1 1
      server/src/main/java/password/pwm/http/servlet/peoplesearch/PhotoDataReader.java
  22. 4 4
      server/src/main/java/password/pwm/http/servlet/resource/CacheEntry.java
  23. 4 5
      server/src/main/java/password/pwm/http/servlet/resource/MemoryFileResource.java
  24. 4 4
      server/src/main/java/password/pwm/http/servlet/resource/ResourceFileServlet.java
  25. 1 2
      server/src/main/java/password/pwm/http/servlet/resource/ResourceServletConfiguration.java
  26. 1 1
      server/src/main/java/password/pwm/http/servlet/resource/ResourceServletService.java
  27. 12 7
      server/src/main/java/password/pwm/ldap/LdapConnectionService.java
  28. 3 3
      server/src/main/java/password/pwm/svc/httpclient/PwmHttpClient.java
  29. 2 2
      server/src/main/java/password/pwm/svc/report/ReportService.java
  30. 3 7
      server/src/main/java/password/pwm/svc/telemetry/TelemetryService.java
  31. 2 6
      server/src/main/java/password/pwm/svc/wordlist/LocalDBWordlistBucket.java
  32. 1 3
      server/src/main/java/password/pwm/util/CaptchaUtility.java
  33. 0 1
      server/src/main/java/password/pwm/util/OnejarHelper.java
  34. 6 5
      server/src/main/java/password/pwm/util/db/DBConfiguration.java
  35. 13 12
      server/src/main/java/password/pwm/util/db/JDBCDriverLoader.java
  36. 39 1
      server/src/main/java/password/pwm/util/java/JavaHelper.java
  37. 6 0
      server/src/main/java/password/pwm/util/localdb/LocalDBFactory.java
  38. 12 9
      server/src/main/java/password/pwm/util/logging/LocalDBLogger.java
  39. 1 3
      server/src/main/java/password/pwm/util/operations/cr/LocalDbCrOperator.java
  40. 1 3
      server/src/main/java/password/pwm/util/password/PasswordUtility.java
  41. 8 0
      server/src/main/java/password/pwm/util/secure/SecureService.java
  42. 1 2
      server/src/main/java/password/pwm/util/secure/self/SelfCertFactory.java
  43. 4 4
      server/src/main/java/password/pwm/ws/server/RestRequest.java
  44. 35 15
      server/src/main/java/password/pwm/ws/server/RestServlet.java

+ 19 - 8
onejar/src/main/java/password/pwm/onejar/OnejarMain.java

@@ -128,7 +128,7 @@ public class OnejarMain
     {
     {
         private final TomcatOnejarRunner runner;
         private final TomcatOnejarRunner runner;
 
 
-        public ShutdownThread( final TomcatOnejarRunner runner )
+        ShutdownThread( final TomcatOnejarRunner runner )
         {
         {
             this.runner = runner;
             this.runner = runner;
         }
         }
@@ -137,7 +137,7 @@ public class OnejarMain
         public void run()
         public void run()
         {
         {
             final Instant startTime = Instant.now();
             final Instant startTime = Instant.now();
-            out("shutdown process initiated");
+            out( "shutdown process initiated" );
             try
             try
             {
             {
                 runner.shutdown();
                 runner.shutdown();
@@ -146,22 +146,33 @@ public class OnejarMain
             {
             {
                 e.printStackTrace();
                 e.printStackTrace();
             }
             }
-            final Duration duration = Duration.between( startTime, Instant.now() );
-            out("shutdown complete (" + duration.toString() + ")" );
+            out( "shutdown complete", startTime );
         }
         }
     }
     }
 
 
-    void out( final String output )
+    void out( final String message )
     {
     {
-        output( output );
+        output( message );
     }
     }
 
 
-    static void output( final String output )
+    void out( final String message, final Instant startTime )
+    {
+        output( message, startTime );
+    }
+
+    static void output( final String message )
     {
     {
         final Instant now = Instant.now().truncatedTo( ChronoUnit.SECONDS );
         final Instant now = Instant.now().truncatedTo( ChronoUnit.SECONDS );
-        System.out.println( now.toString() + ", OneJar, " + output );
+        System.out.println( now.toString() + ", OneJar, " + message );
     }
     }
 
 
+    static void output( final String message, final Instant startTime )
+    {
+        final Duration duration = Duration.between( Instant.now(), startTime );
+        output( message + " (" + duration.toString() + ")" );
+    }
+
+
     private void explodeWar( final OnejarConfig onejarConfig ) throws IOException
     private void explodeWar( final OnejarConfig onejarConfig ) throws IOException
     {
     {
         final InputStream warSource = onejarConfig.getWar();
         final InputStream warSource = onejarConfig.getWar();

+ 16 - 11
onejar/src/main/java/password/pwm/onejar/TomcatOnejarRunner.java

@@ -42,7 +42,6 @@ import java.net.URLClassLoader;
 import java.nio.charset.StandardCharsets;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Files;
 import java.nio.file.Paths;
 import java.nio.file.Paths;
-import java.time.Duration;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
@@ -117,7 +116,7 @@ public class TomcatOnejarRunner
         {
         {
             tomcat.setConnector( makeConnector( onejarConfig, tlsProperties ) );
             tomcat.setConnector( makeConnector( onejarConfig, tlsProperties ) );
             tomcat.start();
             tomcat.start();
-            out( "tomcat started in " + Duration.between( Instant.now(), startTime ).toString() );
+            out( "tomcat started", startTime );
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )
         {
         {
@@ -163,22 +162,22 @@ public class TomcatOnejarRunner
         connector.setSecure( true );
         connector.setSecure( true );
         connector.setScheme( "https" );
         connector.setScheme( "https" );
         connector.addUpgradeProtocol( new Http2Protocol() );
         connector.addUpgradeProtocol( new Http2Protocol() );
-        connector.setAttribute( "SSLEnabled", "true" );
+        connector.setProperty( "SSLEnabled", "true" );
        // connector.setAttribute( "truststoreType", "PKCS12" );
        // connector.setAttribute( "truststoreType", "PKCS12" );
-        connector.setAttribute( "keystoreFile", onejarConfig.getKeystoreFile().getAbsolutePath() );
-        connector.setAttribute( "keystorePass", onejarConfig.getKeystorePass() );
-        connector.setAttribute( "keyAlias", OnejarMain.KEYSTORE_ALIAS );
-        connector.setAttribute( "clientAuth", "false" );
+        connector.setProperty( "keystoreFile", onejarConfig.getKeystoreFile().getAbsolutePath() );
+        connector.setProperty( "keystorePass", onejarConfig.getKeystorePass() );
+        connector.setProperty( "keyAlias", OnejarMain.KEYSTORE_ALIAS );
+        connector.setProperty( "clientAuth", "false" );
 
 
-        out( "connector maxThreads=" + connector.getAttribute( "maxThreads" ) );
-        out( "connector maxConnections=" + connector.getAttribute( "maxConnections" ) );
+        out( "connector maxThreads=" + connector.getProperty( "maxThreads" ) );
+        out( "connector maxConnections=" + connector.getProperty( "maxConnections" ) );
 
 
         if ( tlsProperties != null )
         if ( tlsProperties != null )
         {
         {
             for ( final String key : tlsProperties.stringPropertyNames() )
             for ( final String key : tlsProperties.stringPropertyNames() )
             {
             {
                 final String value = tlsProperties.getProperty( key );
                 final String value = tlsProperties.getProperty( key );
-                connector.setAttribute( key, value );
+                connector.setProperty( key, value );
             }
             }
         }
         }
 
 
@@ -215,10 +214,16 @@ public class TomcatOnejarRunner
         onejarMain.out( output );
         onejarMain.out( output );
     }
     }
 
 
+    void out( final String output, final Instant startTime )
+    {
+        onejarMain.out( output, startTime );
+    }
 
 
     Properties executeOnejarHelper( final OnejarConfig onejarConfig )
     Properties executeOnejarHelper( final OnejarConfig onejarConfig )
             throws IOException, ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException
             throws IOException, ClassNotFoundException, IllegalAccessException, NoSuchMethodException, InvocationTargetException
     {
     {
+        final Instant startTime = Instant.now();
+
         try ( URLClassLoader classLoader = warClassLoaderFromConfig( onejarConfig ) )
         try ( URLClassLoader classLoader = warClassLoaderFromConfig( onejarConfig ) )
         {
         {
             final Class pwmMainClass = classLoader.loadClass( "password.pwm.util.OnejarHelper" );
             final Class pwmMainClass = classLoader.loadClass( "password.pwm.util.OnejarHelper" );
@@ -240,7 +245,7 @@ public class TomcatOnejarRunner
 
 
             final Object returnObjValue = mainMethod.invoke( null, arguments );
             final Object returnObjValue = mainMethod.invoke( null, arguments );
             final Properties returnProps = ( Properties ) returnObjValue;
             final Properties returnProps = ( Properties ) returnObjValue;
-            out( "completed read of tlsProperties " );
+            out( "completed read of tlsProperties", startTime );
             return returnProps;
             return returnProps;
         }
         }
     }
     }

+ 32 - 32
server/src/main/java/password/pwm/PwmApplication.java

@@ -75,6 +75,7 @@ import password.pwm.util.db.DatabaseService;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.FileSystemUtility;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDB;
 import password.pwm.util.localdb.LocalDBFactory;
 import password.pwm.util.localdb.LocalDBFactory;
@@ -104,6 +105,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map;
+import java.util.Optional;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Supplier;
 import java.util.function.Supplier;
@@ -118,7 +120,7 @@ public class PwmApplication
 {
 {
     private static final PwmLogger LOGGER = PwmLogger.forClass( PwmApplication.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( PwmApplication.class );
     private static final String DEFAULT_INSTANCE_ID = "-1";
     private static final String DEFAULT_INSTANCE_ID = "-1";
-    
+
     private final Instant startupTime = Instant.now();
     private final Instant startupTime = Instant.now();
     private final AtomicInteger activeServletRequests = new AtomicInteger( 0 );
     private final AtomicInteger activeServletRequests = new AtomicInteger( 0 );
     private final PwmServiceManager pwmServiceManager = new PwmServiceManager();
     private final PwmServiceManager pwmServiceManager = new PwmServiceManager();
@@ -145,7 +147,7 @@ public class PwmApplication
         }
         }
         catch ( final PwmUnrecoverableException e )
         catch ( final PwmUnrecoverableException e )
         {
         {
-            LOGGER.fatal( () -> e.getMessage() );
+            LOGGER.fatal( e::getMessage );
             throw e;
             throw e;
         }
         }
     }
     }
@@ -686,14 +688,14 @@ public class PwmApplication
         {
         {
             try
             try
             {
             {
-                final String storedDateStr = readAppAttribute( AppAttribute.INSTALL_DATE, String.class );
-                if ( storedDateStr == null || storedDateStr.length() < 1 )
+                final Optional<String> storedDateStr = readAppAttribute( AppAttribute.INSTALL_DATE, String.class );
+                if ( !storedDateStr.isPresent() )
                 {
                 {
                     writeAppAttribute( AppAttribute.INSTALL_DATE, String.valueOf( startupTime.toEpochMilli() ) );
                     writeAppAttribute( AppAttribute.INSTALL_DATE, String.valueOf( startupTime.toEpochMilli() ) );
                 }
                 }
                 else
                 else
                 {
                 {
-                    return Instant.ofEpochMilli( Long.parseLong( storedDateStr ) );
+                    return Instant.ofEpochMilli( Long.parseLong( storedDateStr.get() ) );
                 }
                 }
             }
             }
             catch ( final Exception e )
             catch ( final Exception e )
@@ -706,37 +708,35 @@ public class PwmApplication
 
 
     private String fetchInstanceID( final LocalDB localDB, final PwmApplication pwmApplication )
     private String fetchInstanceID( final LocalDB localDB, final PwmApplication pwmApplication )
     {
     {
-        String newInstanceID = pwmApplication.getPwmEnvironment().getParameters().get( PwmEnvironment.ApplicationParameter.InstanceID );
-
-        if ( newInstanceID != null && newInstanceID.trim().length() > 0 )
         {
         {
-            return newInstanceID;
-        }
-
-        newInstanceID = readAppAttribute( AppAttribute.INSTANCE_ID, String.class );
-
-        if ( newInstanceID == null || newInstanceID.length() < 1 )
-        {
-            final PwmRandom pwmRandom = PwmRandom.getInstance();
-            newInstanceID = Long.toHexString( pwmRandom.nextLong() ).toUpperCase();
-
-            final String finalInstanceID = newInstanceID;
-            LOGGER.debug( () -> "generated new random instanceID " + finalInstanceID );
+            final String newInstanceID = pwmApplication.getPwmEnvironment().getParameters().get( PwmEnvironment.ApplicationParameter.InstanceID );
 
 
-            if ( localDB != null )
+            if ( !StringUtil.isTrimEmpty( newInstanceID ) )
             {
             {
-                writeAppAttribute( AppAttribute.INSTANCE_ID, newInstanceID );
+                return newInstanceID;
             }
             }
         }
         }
-        else
+
         {
         {
-            final String id = newInstanceID;
-            LOGGER.trace( () -> "retrieved instanceID " + id + "" + " from localDB" );
+            final Optional<String> optionalStoredInstanceID = readAppAttribute( AppAttribute.INSTANCE_ID, String.class );
+            if ( optionalStoredInstanceID.isPresent() )
+            {
+                final String instanceID = optionalStoredInstanceID.get();
+                if ( !StringUtil.isTrimEmpty( instanceID ) )
+                {
+                    LOGGER.trace( () -> "retrieved instanceID " + instanceID + "" + " from localDB" );
+                    return instanceID;
+                }
+            }
         }
         }
 
 
-        if ( newInstanceID.length() < 1 )
+        final PwmRandom pwmRandom = PwmRandom.getInstance();
+        final String newInstanceID = Long.toHexString( pwmRandom.nextLong() ).toUpperCase();
+        LOGGER.debug( () -> "generated new random instanceID " + newInstanceID );
+
+        if ( localDB != null )
         {
         {
-            newInstanceID = DEFAULT_INSTANCE_ID;
+            writeAppAttribute( AppAttribute.INSTANCE_ID, newInstanceID );
         }
         }
 
 
         return newInstanceID;
         return newInstanceID;
@@ -939,29 +939,29 @@ public class PwmApplication
         return runtimeNonce;
         return runtimeNonce;
     }
     }
 
 
-    public <T extends Serializable> T readAppAttribute( final AppAttribute appAttribute, final Class<T> returnClass )
+    public <T extends Serializable> Optional<T> readAppAttribute( final AppAttribute appAttribute, final Class<T> returnClass )
     {
     {
         if ( localDB == null || localDB.status() != LocalDB.Status.OPEN )
         if ( localDB == null || localDB.status() != LocalDB.Status.OPEN )
         {
         {
             LOGGER.debug( () -> "error retrieving key '" + appAttribute.getKey() + "', localDB unavailable: " );
             LOGGER.debug( () -> "error retrieving key '" + appAttribute.getKey() + "', localDB unavailable: " );
-            return null;
+            return Optional.empty();
         }
         }
 
 
         if ( appAttribute == null )
         if ( appAttribute == null )
         {
         {
-            return null;
+            return Optional.empty();
         }
         }
 
 
         try
         try
         {
         {
             final String strValue = localDB.get( LocalDB.DB.PWM_META, appAttribute.getKey() );
             final String strValue = localDB.get( LocalDB.DB.PWM_META, appAttribute.getKey() );
-            return JsonUtil.deserialize( strValue, returnClass );
+            return Optional.of( JsonUtil.deserialize( strValue, returnClass ) );
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )
         {
         {
             LOGGER.error( () -> "error retrieving key '" + appAttribute.getKey() + "' value from localDB: " + e.getMessage() );
             LOGGER.error( () -> "error retrieving key '" + appAttribute.getKey() + "' value from localDB: " + e.getMessage() );
         }
         }
-        return null;
+        return Optional.empty();
     }
     }
 
 
     public void writeAppAttribute( final AppAttribute appAttribute, final Serializable value )
     public void writeAppAttribute( final AppAttribute appAttribute, final Serializable value )

+ 23 - 29
server/src/main/java/password/pwm/bean/UserIdentity.java

@@ -33,11 +33,13 @@ import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.svc.cache.CacheKey;
 import password.pwm.svc.cache.CacheKey;
 import password.pwm.svc.cache.CachePolicy;
 import password.pwm.svc.cache.CachePolicy;
 import password.pwm.svc.cache.CacheService;
 import password.pwm.svc.cache.CacheService;
+import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 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 java.io.Serializable;
 import java.io.Serializable;
+import java.util.Objects;
 import java.util.StringTokenizer;
 import java.util.StringTokenizer;
 
 
 @SuppressFBWarnings( "SE_TRANSIENT_FIELD_NOT_RESTORED" )
 @SuppressFBWarnings( "SE_TRANSIENT_FIELD_NOT_RESTORED" )
@@ -49,19 +51,15 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
     private static final String DELIM_SEPARATOR = "|";
     private static final String DELIM_SEPARATOR = "|";
 
 
     private transient String obfuscatedValue;
     private transient String obfuscatedValue;
-    private transient boolean canonicalized;
+    private transient boolean canonical;
 
 
     private final String userDN;
     private final String userDN;
     private final String ldapProfile;
     private final String ldapProfile;
 
 
     public UserIdentity( final String userDN, final String ldapProfile )
     public UserIdentity( final String userDN, final String ldapProfile )
     {
     {
-        if ( userDN == null || userDN.length() < 1 )
-        {
-            throw new IllegalArgumentException( "UserIdentity: userDN value cannot be empty" );
-        }
-        this.userDN = userDN;
-        this.ldapProfile = ldapProfile == null ? "" : ldapProfile;
+        this.userDN = JavaHelper.requireNonEmpty( userDN, "UserIdentity: userDN value cannot be empty" );
+        this.ldapProfile = JavaHelper.requireNonEmpty( ldapProfile, "UserIdentity: ldapProfile value cannot be empty" );
     }
     }
 
 
     public UserIdentity( final String userDN, final String ldapProfile, final boolean canonical )
     public UserIdentity( final String userDN, final String ldapProfile, final boolean canonical )
@@ -72,7 +70,7 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
         }
         }
         this.userDN = userDN;
         this.userDN = userDN;
         this.ldapProfile = ldapProfile == null ? "" : ldapProfile;
         this.ldapProfile = ldapProfile == null ? "" : ldapProfile;
-        this.canonicalized = true;
+        this.canonical = canonical;
     }
     }
 
 
     public String getUserDN( )
     public String getUserDN( )
@@ -87,11 +85,13 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
 
 
     public LdapProfile getLdapProfile( final Configuration configuration )
     public LdapProfile getLdapProfile( final Configuration configuration )
     {
     {
-        if ( configuration == null )
+        Objects.requireNonNull( configuration );
+        final LdapProfile ldapProfile = configuration.getLdapProfiles().get( this.getLdapProfileID() );
+        if ( ldapProfile == null )
         {
         {
-            return null;
+            throw new IllegalStateException( "bogus ldapProfileID on userIdentity: "  + this.getLdapProfileID() );
         }
         }
-        return configuration.getLdapProfiles().getOrDefault( this.getLdapProfileID(), null );
+        return ldapProfile;
     }
     }
 
 
     public String toString( )
     public String toString( )
@@ -144,12 +144,11 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
         return this.getUserDN() + ( ( this.getLdapProfileID() != null && !this.getLdapProfileID().isEmpty() ) ? " (" + this.getLdapProfileID() + ")" : "" );
         return this.getUserDN() + ( ( this.getLdapProfileID() != null && !this.getLdapProfileID().isEmpty() ) ? " (" + this.getLdapProfileID() + ")" : "" );
     }
     }
 
 
-    public static UserIdentity fromObfuscatedKey( final String key, final PwmApplication pwmApplication ) throws PwmUnrecoverableException
+    public static UserIdentity fromObfuscatedKey( final String key, final PwmApplication pwmApplication )
+            throws PwmUnrecoverableException
     {
     {
-        if ( key == null || key.length() < 1 )
-        {
-            return null;
-        }
+        Objects.requireNonNull( pwmApplication );
+        JavaHelper.requireNonEmpty( key, "key can not be null or empty" );
 
 
         if ( !key.startsWith( CRYPO_HEADER ) )
         if ( !key.startsWith( CRYPO_HEADER ) )
         {
         {
@@ -168,12 +167,10 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
         }
         }
     }
     }
 
 
-    public static UserIdentity fromDelimitedKey( final String key ) throws PwmUnrecoverableException
+    public static UserIdentity fromDelimitedKey( final String key )
+            throws PwmUnrecoverableException
     {
     {
-        if ( key == null || key.length() < 1 )
-        {
-            return null;
-        }
+        JavaHelper.requireNonEmpty( key );
 
 
         final StringTokenizer st = new StringTokenizer( key, DELIM_SEPARATOR );
         final StringTokenizer st = new StringTokenizer( key, DELIM_SEPARATOR );
         if ( st.countTokens() < 2 )
         if ( st.countTokens() < 2 )
@@ -189,13 +186,10 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
         return new UserIdentity( userDN, profileID );
         return new UserIdentity( userDN, profileID );
     }
     }
 
 
-    public static UserIdentity fromKey( final String key, final PwmApplication pwmApplication ) throws PwmUnrecoverableException
+    public static UserIdentity fromKey( final String key, final PwmApplication pwmApplication )
+            throws PwmUnrecoverableException
     {
     {
-        if ( key == null || key.length() < 1 )
-        {
-            final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_MISSING_PARAMETER, "userKey parameter is missing" );
-            throw new PwmUnrecoverableException( errorInformation );
-        }
+        JavaHelper.requireNonEmpty( key );
 
 
         if ( key.startsWith( CRYPO_HEADER ) )
         if ( key.startsWith( CRYPO_HEADER ) )
         {
         {
@@ -266,7 +260,7 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
     public UserIdentity canonicalized( final PwmApplication pwmApplication )
     public UserIdentity canonicalized( final PwmApplication pwmApplication )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
-        if ( this.canonicalized )
+        if ( this.canonical )
         {
         {
             return this;
             return this;
         }
         }
@@ -282,7 +276,7 @@ public class UserIdentity implements Serializable, Comparable<UserIdentity>
             throw PwmUnrecoverableException.fromChaiException( e );
             throw PwmUnrecoverableException.fromChaiException( e );
         }
         }
         final UserIdentity canonicalziedIdentity = new UserIdentity( userDN, this.getLdapProfileID() );
         final UserIdentity canonicalziedIdentity = new UserIdentity( userDN, this.getLdapProfileID() );
-        canonicalziedIdentity.canonicalized = true;
+        canonicalziedIdentity.canonical = true;
         return canonicalziedIdentity;
         return canonicalziedIdentity;
     }
     }
 }
 }

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

@@ -74,7 +74,6 @@ import password.pwm.util.secure.SecureService;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.InvocationTargetException;
 import java.security.cert.X509Certificate;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedHashSet;
@@ -134,20 +133,8 @@ public class Configuration implements SettingReader
 
 
     public EmailItemBean readSettingAsEmail( final PwmSetting setting, final Locale locale )
     public EmailItemBean readSettingAsEmail( final PwmSetting setting, final Locale locale )
     {
     {
-        if ( PwmSettingSyntax.EMAIL != setting.getSyntax() )
-        {
-            throw new IllegalArgumentException( "may not read EMAIL value for setting: " + setting.toString() );
-        }
-
-        final Map<String, EmailItemBean> storedValues = ( Map<String, EmailItemBean> ) readStoredValue( setting ).toNativeObject();
-        final Map<Locale, EmailItemBean> availableLocaleMap = new LinkedHashMap<>();
-        for ( final Map.Entry<String, EmailItemBean> entry : storedValues.entrySet() )
-        {
-            final String localeStr = entry.getKey();
-            availableLocaleMap.put( LocaleHelper.parseLocaleString( localeStr ), entry.getValue() );
-        }
+        final Map<Locale, EmailItemBean> availableLocaleMap = ValueTypeConverter.valueToLocalizedEmail( setting, readStoredValue( setting ) );
         final Locale matchedLocale = LocaleHelper.localeResolver( locale, availableLocaleMap.keySet() );
         final Locale matchedLocale = LocaleHelper.localeResolver( locale, availableLocaleMap.keySet() );
-
         return availableLocaleMap.get( matchedLocale );
         return availableLocaleMap.get( matchedLocale );
     }
     }
 
 
@@ -364,34 +351,6 @@ public class Configuration implements SettingReader
         return storedConfiguration.isDefaultValue( pwmSetting, null );
         return storedConfiguration.isDefaultValue( pwmSetting, null );
     }
     }
 
 
-    public Collection<Locale> localesForSetting( final PwmSetting setting )
-    {
-        final Collection<Locale> returnCollection = new ArrayList<>();
-        switch ( setting.getSyntax() )
-        {
-            case LOCALIZED_TEXT_AREA:
-            case LOCALIZED_STRING:
-                for ( final String localeStr : ( ( Map<String, String> ) readStoredValue( setting ).toNativeObject() ).keySet() )
-                {
-                    returnCollection.add( LocaleHelper.parseLocaleString( localeStr ) );
-                }
-                break;
-
-            case LOCALIZED_STRING_ARRAY:
-                for ( final String localeStr : ( ( Map<String, List<String>> ) readStoredValue( setting ).toNativeObject() ).keySet() )
-                {
-                    returnCollection.add( LocaleHelper.parseLocaleString( localeStr ) );
-                }
-                break;
-
-            default:
-                // ignore other types
-                break;
-        }
-
-        return returnCollection;
-    }
-
     public boolean readSettingAsBoolean( final PwmSetting setting )
     public boolean readSettingAsBoolean( final PwmSetting setting )
     {
     {
         return ValueTypeConverter.valueToBoolean( readStoredValue( setting ) );
         return ValueTypeConverter.valueToBoolean( readStoredValue( setting ) );
@@ -424,13 +383,11 @@ public class Configuration implements SettingReader
 
 
     public PwmSecurityKey getSecurityKey( ) throws PwmUnrecoverableException
     public PwmSecurityKey getSecurityKey( ) throws PwmUnrecoverableException
     {
     {
-
         return configurationSuppliers.pwmSecurityKey.call();
         return configurationSuppliers.pwmSecurityKey.call();
     }
     }
 
 
     public List<DataStorageMethod> getResponseStorageLocations( final PwmSetting setting )
     public List<DataStorageMethod> getResponseStorageLocations( final PwmSetting setting )
     {
     {
-
         return getGenericStorageLocations( setting );
         return getGenericStorageLocations( setting );
     }
     }
 
 

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

@@ -36,7 +36,7 @@ public class ConfigurationUtil
     public static List<DataStorageMethod> getCrReadPreference( final Configuration configuration )
     public static List<DataStorageMethod> getCrReadPreference( final Configuration configuration )
     {
     {
         final List<DataStorageMethod> readPreferences = configuration.getResponseStorageLocations( PwmSetting.FORGOTTEN_PASSWORD_READ_PREFERENCE );
         final List<DataStorageMethod> readPreferences = configuration.getResponseStorageLocations( PwmSetting.FORGOTTEN_PASSWORD_READ_PREFERENCE );
-        if ( readPreferences.size() == 1 && readPreferences.get( 0 ) == DataStorageMethod.AUTO )
+        if ( readPreferences.size() == 1 && readPreferences.iterator().next() == DataStorageMethod.AUTO )
         {
         {
             readPreferences.clear();
             readPreferences.clear();
             if ( configuration.hasDbConfigured() )
             if ( configuration.hasDbConfigured() )

+ 6 - 15
server/src/main/java/password/pwm/config/profile/AbstractProfile.java

@@ -20,13 +20,11 @@
 
 
 package password.pwm.config.profile;
 package password.pwm.config.profile;
 
 
-import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.SettingReader;
 import password.pwm.config.SettingReader;
-import password.pwm.config.value.StoredValue;
 import password.pwm.config.option.IdentityVerificationMethod;
 import password.pwm.config.option.IdentityVerificationMethod;
-import password.pwm.config.option.MessageSendMethod;
 import password.pwm.config.stored.StoredConfiguration;
 import password.pwm.config.stored.StoredConfiguration;
+import password.pwm.config.value.StoredValue;
 import password.pwm.config.value.ValueTypeConverter;
 import password.pwm.config.value.ValueTypeConverter;
 import password.pwm.config.value.VerificationMethodValue;
 import password.pwm.config.value.VerificationMethodValue;
 import password.pwm.config.value.data.ActionConfiguration;
 import password.pwm.config.value.data.ActionConfiguration;
@@ -36,7 +34,7 @@ import password.pwm.util.PasswordData;
 
 
 import java.security.cert.X509Certificate;
 import java.security.cert.X509Certificate;
 import java.util.Collections;
 import java.util.Collections;
-import java.util.LinkedHashSet;
+import java.util.EnumSet;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
 import java.util.Optional;
 import java.util.Optional;
@@ -95,14 +93,7 @@ public abstract class AbstractProfile implements Profile, SettingReader
     @Override
     @Override
     public <E extends Enum<E>> E readSettingAsEnum( final PwmSetting setting, final Class<E> enumClass )
     public <E extends Enum<E>> E readSettingAsEnum( final PwmSetting setting, final Class<E> enumClass )
     {
     {
-        final StoredValue value = readSetting( setting );
-        final E returnValue = ValueTypeConverter.valueToEnum( setting, value, enumClass );
-        if ( MessageSendMethod.class.equals( enumClass ) )
-        {
-            Configuration.deprecatedSettingException( setting, this.getIdentifier(), ( MessageSendMethod ) returnValue );
-        }
-
-        return returnValue;
+        return ValueTypeConverter.valueToEnum( setting, readSetting( setting ), enumClass );
     }
     }
 
 
     public List<ActionConfiguration> readSettingAsAction( final PwmSetting setting )
     public List<ActionConfiguration> readSettingAsAction( final PwmSetting setting )
@@ -141,7 +132,7 @@ public abstract class AbstractProfile implements Profile, SettingReader
     }
     }
 
 
     @Override
     @Override
-    public List<UserPermission> getPermissionMatches( )
+    public List<UserPermission> profilePermissions( )
     {
     {
         final Optional<PwmSetting> optionalQueryMatchSetting = profileType().getQueryMatch();
         final Optional<PwmSetting> optionalQueryMatchSetting = profileType().getQueryMatch();
         if ( optionalQueryMatchSetting.isPresent() )
         if ( optionalQueryMatchSetting.isPresent() )
@@ -158,7 +149,7 @@ public abstract class AbstractProfile implements Profile, SettingReader
 
 
     Set<IdentityVerificationMethod> readVerificationMethods( final PwmSetting setting, final VerificationMethodValue.EnabledState enabledState )
     Set<IdentityVerificationMethod> readVerificationMethods( final PwmSetting setting, final VerificationMethodValue.EnabledState enabledState )
     {
     {
-        final Set<IdentityVerificationMethod> result = new LinkedHashSet<>();
+        final Set<IdentityVerificationMethod> result = EnumSet.noneOf( IdentityVerificationMethod.class );
         final StoredValue configValue = readSetting( setting );
         final StoredValue configValue = readSetting( setting );
         final VerificationMethodValue.VerificationMethodSettings verificationMethodSettings = ( VerificationMethodValue.VerificationMethodSettings ) configValue.toNativeObject();
         final VerificationMethodValue.VerificationMethodSettings verificationMethodSettings = ( VerificationMethodValue.VerificationMethodSettings ) configValue.toNativeObject();
 
 
@@ -179,7 +170,7 @@ public abstract class AbstractProfile implements Profile, SettingReader
     {
     {
         if ( !setting.getCategory().hasProfiles() )
         if ( !setting.getCategory().hasProfiles() )
         {
         {
-            throw new IllegalStateException( "attempt to read non-profiled setting via profile" );
+            throw new IllegalStateException( "attempt to read non-profiled setting '" + setting.getKey() + "' via profile" );
         }
         }
         return storedConfiguration.readSetting( setting, getIdentifier() );
         return storedConfiguration.readSetting( setting, getIdentifier() );
     }
     }

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

@@ -290,7 +290,7 @@ public class ChallengeProfile implements Profile, Serializable
     }
     }
 
 
     @Override
     @Override
-    public List<UserPermission> getPermissionMatches( )
+    public List<UserPermission> profilePermissions( )
     {
     {
         throw new UnsupportedOperationException();
         throw new UnsupportedOperationException();
     }
     }

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

@@ -124,7 +124,7 @@ public class LdapProfile extends AbstractProfile implements Profile
     }
     }
 
 
     @Override
     @Override
-    public List<UserPermission> getPermissionMatches( )
+    public List<UserPermission> profilePermissions( )
     {
     {
         throw new UnsupportedOperationException();
         throw new UnsupportedOperationException();
     }
     }

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

@@ -34,7 +34,7 @@ public interface Profile
 
 
     String getDisplayName( Locale locale );
     String getDisplayName( Locale locale );
 
 
-    List<UserPermission> getPermissionMatches( );
+    List<UserPermission> profilePermissions( );
 
 
     interface ProfileFactory
     interface ProfileFactory
     {
     {

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

@@ -80,7 +80,7 @@ public class ProfileUtility
         final Map<String, Profile> profileMap = pwmApplication.getConfig().profileMap( profileDefinition );
         final Map<String, Profile> profileMap = pwmApplication.getConfig().profileMap( profileDefinition );
         for ( final Profile profile : profileMap.values() )
         for ( final Profile profile : profileMap.values() )
         {
         {
-            final List<UserPermission> queryMatches = profile.getPermissionMatches();
+            final List<UserPermission> queryMatches = profile.profilePermissions();
             final boolean match = UserPermissionUtility.testUserPermission( pwmApplication, sessionLabel, userIdentity, queryMatches );
             final boolean match = UserPermissionUtility.testUserPermission( pwmApplication, sessionLabel, userIdentity, queryMatches );
             if ( match )
             if ( match )
             {
             {

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

@@ -342,7 +342,7 @@ public class PwmPasswordPolicy implements Profile, Serializable
     }
     }
 
 
     @Override
     @Override
-    public List<UserPermission> getPermissionMatches( )
+    public List<UserPermission> profilePermissions( )
     {
     {
         throw new UnsupportedOperationException();
         throw new UnsupportedOperationException();
     }
     }

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

@@ -275,7 +275,7 @@ public class StoredConfigZipJsonSerializer implements StoredConfigSerializer
     private String hash( final FileValue.FileContent fileContent )
     private String hash( final FileValue.FileContent fileContent )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
-        return SecureEngine.hash( fileContent.getContents().copyOf(), PwmHashAlgorithm.SHA256 );
+        return SecureEngine.hash( fileContent.getContents().newByteArrayInputStream(), PwmHashAlgorithm.SHA256 );
     }
     }
 
 
 
 

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

@@ -37,7 +37,6 @@ import password.pwm.util.secure.PwmHashAlgorithm;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.SecureEngine;
 import password.pwm.util.secure.SecureEngine;
 
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.IOException;
 import java.io.Serializable;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -86,12 +85,12 @@ public class FileValue extends AbstractValue implements StoredValue
         String sha512sum( )
         String sha512sum( )
                 throws PwmUnrecoverableException
                 throws PwmUnrecoverableException
         {
         {
-            return SecureEngine.hash( new ByteArrayInputStream( contents.copyOf() ), PwmHashAlgorithm.SHA512 );
+            return SecureEngine.hash( contents.newByteArrayInputStream(), PwmHashAlgorithm.SHA512 );
         }
         }
 
 
         public int size( )
         public int size( )
         {
         {
-            return contents.copyOf().length;
+            return contents.size();
         }
         }
     }
     }
 
 

+ 19 - 0
server/src/main/java/password/pwm/config/value/ValueTypeConverter.java

@@ -21,6 +21,7 @@
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
+import password.pwm.bean.EmailItemBean;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSettingSyntax;
 import password.pwm.config.PwmSettingSyntax;
 import password.pwm.config.value.data.ActionConfiguration;
 import password.pwm.config.value.data.ActionConfiguration;
@@ -284,6 +285,24 @@ public final class ValueTypeConverter
         return JavaHelper.readEnumFromString( enumClass, strValue ).orElse( null );
         return JavaHelper.readEnumFromString( enumClass, strValue ).orElse( null );
     }
     }
 
 
+    public static Map<Locale, EmailItemBean> valueToLocalizedEmail( final PwmSetting setting, final StoredValue storedValue )
+    {
+        if ( PwmSettingSyntax.EMAIL != setting.getSyntax() )
+        {
+            throw new IllegalArgumentException( "may not read EMAIL value for setting: " + setting.toString() );
+        }
+
+        final Map<String, EmailItemBean> storedValues =  ( Map<String, EmailItemBean> ) storedValue.toNativeObject();
+        final Map<Locale, EmailItemBean> availableLocaleMap = new LinkedHashMap<>();
+        for ( final Map.Entry<String, EmailItemBean> entry : storedValues.entrySet() )
+        {
+            final String localeStr = entry.getKey();
+            availableLocaleMap.put( LocaleHelper.parseLocaleString( localeStr ), entry.getValue() );
+        }
+
+        return Collections.unmodifiableMap( availableLocaleMap );
+    }
+
     public static Map<FileValue.FileInformation, FileValue.FileContent> valueToFile( final PwmSetting setting, final StoredValue storedValue )
     public static Map<FileValue.FileInformation, FileValue.FileContent> valueToFile( final PwmSetting setting, final StoredValue storedValue )
     {
     {
         if ( PwmSettingSyntax.FILE != setting.getSyntax() )
         if ( PwmSettingSyntax.FILE != setting.getSyntax() )

+ 10 - 9
server/src/main/java/password/pwm/config/value/VerificationMethodValue.java

@@ -28,6 +28,7 @@ import password.pwm.config.stored.XmlOutputProcessData;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.i18n.Display;
 import password.pwm.i18n.Display;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
+import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.XmlElement;
 import password.pwm.util.java.XmlElement;
 import password.pwm.util.java.XmlFactory;
 import password.pwm.util.java.XmlFactory;
@@ -37,8 +38,6 @@ import password.pwm.util.secure.PwmSecurityKey;
 import java.io.Serializable;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map;
@@ -50,7 +49,6 @@ public class VerificationMethodValue extends AbstractValue implements StoredValu
 
 
     private final VerificationMethodSettings value;
     private final VerificationMethodSettings value;
 
 
-
     public enum EnabledState
     public enum EnabledState
     {
     {
         disabled,
         disabled,
@@ -69,17 +67,18 @@ public class VerificationMethodValue extends AbstractValue implements StoredValu
             minOptionalRequired = 0;
             minOptionalRequired = 0;
         }
         }
 
 
-        public VerificationMethodSettings( final Map<IdentityVerificationMethod, VerificationMethodSetting> methodSettings, final int minOptionalRequired )
+        public VerificationMethodSettings(
+                final Map<IdentityVerificationMethod, VerificationMethodSetting> methodSettings,
+                final int minOptionalRequired
+        )
         {
         {
-            this.methodSettings = methodSettings == null ? Collections.emptyMap() : Collections.unmodifiableMap( methodSettings );
+            this.methodSettings = Collections.unmodifiableMap( JavaHelper.copiedEnumMap( methodSettings, IdentityVerificationMethod.class ) );
             this.minOptionalRequired = minOptionalRequired;
             this.minOptionalRequired = minOptionalRequired;
         }
         }
 
 
         public Map<IdentityVerificationMethod, VerificationMethodSetting> getMethodSettings( )
         public Map<IdentityVerificationMethod, VerificationMethodSetting> getMethodSettings( )
         {
         {
-            final Map<IdentityVerificationMethod, VerificationMethodSetting> tempMap = new LinkedHashMap<>( methodSettings );
-            tempMap.remove( null );
-            return Collections.unmodifiableMap( tempMap );
+            return methodSettings;
         }
         }
 
 
         public int getMinOptionalRequired( )
         public int getMinOptionalRequired( )
@@ -106,7 +105,9 @@ public class VerificationMethodValue extends AbstractValue implements StoredValu
 
 
     private static VerificationMethodSettings normalizeSettings( final VerificationMethodSettings input )
     private static VerificationMethodSettings normalizeSettings( final VerificationMethodSettings input )
     {
     {
-        final Map<IdentityVerificationMethod, VerificationMethodValue.VerificationMethodSetting> tempMap = new HashMap<>( input.getMethodSettings() );
+        final Map<IdentityVerificationMethod, VerificationMethodValue.VerificationMethodSetting> tempMap = JavaHelper.copiedEnumMap(
+                input.getMethodSettings(),
+                IdentityVerificationMethod.class );
 
 
         for ( final IdentityVerificationMethod recoveryVerificationMethods : IdentityVerificationMethod.availableValues() )
         for ( final IdentityVerificationMethod recoveryVerificationMethods : IdentityVerificationMethod.availableValues() )
         {
         {

+ 6 - 5
server/src/main/java/password/pwm/http/HttpContentType.java

@@ -25,6 +25,7 @@ import password.pwm.util.java.StringUtil;
 
 
 import java.nio.charset.Charset;
 import java.nio.charset.Charset;
 import java.util.List;
 import java.util.List;
+import java.util.Optional;
 
 
 public enum HttpContentType
 public enum HttpContentType
 {
 {
@@ -75,11 +76,11 @@ public enum HttpContentType
         return dataType;
         return dataType;
     }
     }
 
 
-    public static HttpContentType fromContentTypeHeader( final String headerValue, final HttpContentType anyMatch )
+    public static Optional<HttpContentType> fromContentTypeHeader( final String headerValue, final HttpContentType anyMatch )
     {
     {
         if ( StringUtil.isEmpty( headerValue ) )
         if ( StringUtil.isEmpty( headerValue ) )
         {
         {
-            return null;
+            return Optional.empty();
         }
         }
 
 
         final List<String> values = StringUtil.splitAndTrim( headerValue, "," );
         final List<String> values = StringUtil.splitAndTrim( headerValue, "," );
@@ -96,7 +97,7 @@ public enum HttpContentType
                 {
                 {
                     if ( mimeValue.equalsIgnoreCase( type ) )
                     if ( mimeValue.equalsIgnoreCase( type ) )
                     {
                     {
-                        return httpContentType;
+                        return Optional.of( httpContentType );
                     }
                     }
                 }
                 }
             }
             }
@@ -106,10 +107,10 @@ public enum HttpContentType
         {
         {
             if ( values.contains( "*/*" ) )
             if ( values.contains( "*/*" ) )
             {
             {
-                return anyMatch;
+                return Optional.of ( anyMatch );
             }
             }
         }
         }
 
 
-        return null;
+        return Optional.empty();
     }
     }
 }
 }

+ 7 - 0
server/src/main/java/password/pwm/http/bean/ImmutableByteArray.java

@@ -20,6 +20,8 @@
 
 
 package password.pwm.http.bean;
 package password.pwm.http.bean;
 
 
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
 import java.io.Serializable;
 import java.io.Serializable;
 import java.util.Arrays;
 import java.util.Arrays;
 
 
@@ -46,6 +48,11 @@ public class ImmutableByteArray implements Serializable
         return bytes == null ? null : Arrays.copyOf( bytes, bytes.length );
         return bytes == null ? null : Arrays.copyOf( bytes, bytes.length );
     }
     }
 
 
+    public InputStream newByteArrayInputStream( )
+    {
+        return new ByteArrayInputStream( bytes == null ? EMPTY.bytes : bytes );
+    }
+
     public int size()
     public int size()
     {
     {
         return bytes == null ? 0 : bytes.length;
         return bytes == null ? 0 : bytes.length;

+ 2 - 2
server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java

@@ -86,8 +86,8 @@ import password.pwm.ws.server.rest.bean.HealthData;
 import javax.mail.MessagingException;
 import javax.mail.MessagingException;
 import javax.servlet.ServletException;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.annotation.WebServlet;
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.Serializable;
 import java.io.Serializable;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -736,7 +736,7 @@ public class ConfigEditorServlet extends ControlledPwmServlet
                 }
                 }
 
 
                 final Map<String, PwmRequest.FileUploadItem> fileUploads = pwmRequest.readFileUploads( maxFileSize, 1 );
                 final Map<String, PwmRequest.FileUploadItem> fileUploads = pwmRequest.readFileUploads( maxFileSize, 1 );
-                final ByteArrayInputStream fileIs = new ByteArrayInputStream( fileUploads.get( PwmConstants.PARAM_FILE_UPLOAD ).getContent().copyOf() );
+                final InputStream fileIs = fileUploads.get( PwmConstants.PARAM_FILE_UPLOAD ).getContent().newByteArrayInputStream();
 
 
                 HttpsServerCertificateManager.importKey(
                 HttpsServerCertificateManager.importKey(
                         modifier,
                         modifier,

+ 2 - 4
server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerLoginServlet.java

@@ -196,10 +196,8 @@ public class ConfigManagerLoginServlet extends AbstractPwmServlet
 
 
     private static ConfigLoginHistory readConfigLoginHistory( final PwmRequest pwmRequest )
     private static ConfigLoginHistory readConfigLoginHistory( final PwmRequest pwmRequest )
     {
     {
-        final ConfigLoginHistory configLoginHistory = pwmRequest.getPwmApplication().readAppAttribute( AppAttribute.CONFIG_LOGIN_HISTORY, ConfigLoginHistory.class );
-        return configLoginHistory == null
-                ? new ConfigLoginHistory()
-                : configLoginHistory;
+       return pwmRequest.getPwmApplication().readAppAttribute( AppAttribute.CONFIG_LOGIN_HISTORY, ConfigLoginHistory.class )
+               .orElseGet( ConfigLoginHistory::new );
     }
     }
 
 
     private static void updateLoginHistory( final PwmRequest pwmRequest, final UserIdentity userIdentity, final boolean successful )
     private static void updateLoginHistory( final PwmRequest pwmRequest, final UserIdentity userIdentity, final boolean successful )

+ 1 - 1
server/src/main/java/password/pwm/http/servlet/peoplesearch/PhotoDataReader.java

@@ -296,7 +296,7 @@ public class PhotoDataReader
 
 
                 if ( photoDataBean.getContents() != null && !photoDataBean.getContents().isEmpty() )
                 if ( photoDataBean.getContents() != null && !photoDataBean.getContents().isEmpty() )
                 {
                 {
-                    outputStream.write( photoDataBean.getContents().copyOf() );
+                    JavaHelper.copy( photoDataBean.getContents().newByteArrayInputStream(), outputStream );
                 }
                 }
             }
             }
         }
         }

+ 4 - 4
server/src/main/java/password/pwm/http/servlet/resource/CacheEntry.java

@@ -30,15 +30,15 @@ final class CacheEntry implements Serializable
     private final ImmutableByteArray entity;
     private final ImmutableByteArray entity;
     private final Map<String, String> headerStrings;
     private final Map<String, String> headerStrings;
 
 
-    CacheEntry( final byte[] entity, final Map<String, String> headerStrings )
+    CacheEntry( final ImmutableByteArray entity, final Map<String, String> headerStrings )
     {
     {
-        this.entity = ImmutableByteArray.of( entity );
+        this.entity = entity;
         this.headerStrings = headerStrings;
         this.headerStrings = headerStrings;
     }
     }
 
 
-    public byte[] getEntity( )
+    public ImmutableByteArray getEntity( )
     {
     {
-        return entity.copyOf();
+        return entity;
     }
     }
 
 
     public Map<String, String> getHeaderStrings( )
     public Map<String, String> getHeaderStrings( )

+ 4 - 5
server/src/main/java/password/pwm/http/servlet/resource/MemoryFileResource.java

@@ -22,31 +22,30 @@ package password.pwm.http.servlet.resource;
 
 
 import password.pwm.http.bean.ImmutableByteArray;
 import password.pwm.http.bean.ImmutableByteArray;
 
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStream;
 
 
 class MemoryFileResource implements FileResource
 class MemoryFileResource implements FileResource
 {
 {
     private final String name;
     private final String name;
-    private final byte[] contents;
+    private final ImmutableByteArray contents;
     private final long lastModified;
     private final long lastModified;
 
 
     MemoryFileResource( final String name, final ImmutableByteArray contents, final long lastModified )
     MemoryFileResource( final String name, final ImmutableByteArray contents, final long lastModified )
     {
     {
         this.name = name;
         this.name = name;
-        this.contents = contents.copyOf();
+        this.contents = contents;
         this.lastModified = lastModified;
         this.lastModified = lastModified;
     }
     }
 
 
     public InputStream getInputStream( ) throws IOException
     public InputStream getInputStream( ) throws IOException
     {
     {
-        return new ByteArrayInputStream( contents );
+        return contents.newByteArrayInputStream();
     }
     }
 
 
     public long length( )
     public long length( )
     {
     {
-        return contents.length;
+        return contents.size();
     }
     }
 
 
     public long lastModified( )
     public long lastModified( )

+ 4 - 4
server/src/main/java/password/pwm/http/servlet/resource/ResourceFileServlet.java

@@ -29,6 +29,7 @@ import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.HttpHeader;
 import password.pwm.http.HttpHeader;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequest;
+import password.pwm.http.bean.ImmutableByteArray;
 import password.pwm.http.servlet.PwmServlet;
 import password.pwm.http.servlet.PwmServlet;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.Statistic;
 import password.pwm.svc.stats.StatisticsManager;
 import password.pwm.svc.stats.StatisticsManager;
@@ -44,7 +45,6 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.servlet.http.HttpServletResponse;
 import java.io.BufferedInputStream;
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStream;
@@ -309,8 +309,8 @@ public class ResourceFileServlet extends HttpServlet implements PwmServlet
                 }
                 }
             }
             }
 
 
-            final byte[] entity = tempOutputStream.toByteArray();
-            headers.put( HttpHeader.ContentLength.getHttpName(), String.valueOf( entity.length ) );
+            final ImmutableByteArray entity = ImmutableByteArray.of( tempOutputStream.toByteArray() );
+            headers.put( HttpHeader.ContentLength.getHttpName(), String.valueOf( entity.size() ) );
             cacheEntry = new CacheEntry( entity, headers );
             cacheEntry = new CacheEntry( entity, headers );
         }
         }
         else
         else
@@ -326,7 +326,7 @@ public class ResourceFileServlet extends HttpServlet implements PwmServlet
 
 
         try ( OutputStream responseOutputStream = response.getOutputStream() )
         try ( OutputStream responseOutputStream = response.getOutputStream() )
         {
         {
-            JavaHelper.copy( new ByteArrayInputStream( cacheEntry.getEntity() ), responseOutputStream );
+            JavaHelper.copy( cacheEntry.getEntity().newByteArrayInputStream(), responseOutputStream );
         }
         }
 
 
         return fromCache;
         return fromCache;

+ 1 - 2
server/src/main/java/password/pwm/http/servlet/resource/ResourceServletConfiguration.java

@@ -31,7 +31,6 @@ import password.pwm.http.bean.ImmutableByteArray;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.File;
 import java.io.IOException;
 import java.io.IOException;
@@ -194,7 +193,7 @@ class ResourceServletConfiguration
     private static Map<String, FileResource> makeMemoryFileMapFromZipInput( final ImmutableByteArray content )
     private static Map<String, FileResource> makeMemoryFileMapFromZipInput( final ImmutableByteArray content )
             throws IOException
             throws IOException
     {
     {
-        final ZipInputStream stream = new ZipInputStream( new ByteArrayInputStream( content.copyOf() ) );
+        final ZipInputStream stream = new ZipInputStream( content.newByteArrayInputStream() );
         final Map<String, FileResource> memoryMap = new HashMap<>();
         final Map<String, FileResource> memoryMap = new HashMap<>();
 
 
         ZipEntry entry;
         ZipEntry entry;

+ 1 - 1
server/src/main/java/password/pwm/http/servlet/resource/ResourceServletService.java

@@ -89,7 +89,7 @@ public class ResourceServletService implements PwmService
         {
         {
             if ( cacheEntry != null && cacheEntry.getEntity() != null )
             if ( cacheEntry != null && cacheEntry.getEntity() != null )
             {
             {
-                cacheByteCount += cacheEntry.getEntity().length;
+                cacheByteCount += cacheEntry.getEntity().size();
             }
             }
         }
         }
         return cacheByteCount;
         return cacheByteCount;

+ 12 - 7
server/src/main/java/password/pwm/ldap/LdapConnectionService.java

@@ -59,6 +59,7 @@ import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.List;
 import java.util.Map;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Objects;
+import java.util.Optional;
 import java.util.Set;
 import java.util.Set;
 import java.util.TreeMap;
 import java.util.TreeMap;
 import java.util.WeakHashMap;
 import java.util.WeakHashMap;
@@ -327,15 +328,19 @@ public class LdapConnectionService implements PwmService
         String lastLdapFailureStr = null;
         String lastLdapFailureStr = null;
         try
         try
         {
         {
-            lastLdapFailureStr = pwmApplication.readAppAttribute( AppAttribute.LAST_LDAP_ERROR, String.class );
-            if ( lastLdapFailureStr != null && lastLdapFailureStr.length() > 0 )
+            final Optional<String> optionalLastLdapError = pwmApplication.readAppAttribute( AppAttribute.LAST_LDAP_ERROR, String.class );
+            if ( optionalLastLdapError.isPresent() )
             {
             {
-                final Map<String, ErrorInformation> fromJson = JsonUtil.deserialize( lastLdapFailureStr, new TypeToken<Map<String, ErrorInformation>>()
+                lastLdapFailureStr = optionalLastLdapError.get();
+                if ( !StringUtil.isEmpty( lastLdapFailureStr ) )
                 {
                 {
-                } );
-                final Map<String, ErrorInformation> returnMap = new HashMap<>( fromJson );
-                returnMap.keySet().retainAll( pwmApplication.getConfig().getLdapProfiles().keySet() );
-                return returnMap;
+                    final Map<String, ErrorInformation> fromJson = JsonUtil.deserialize( lastLdapFailureStr, new TypeToken<Map<String, ErrorInformation>>()
+                    {
+                    } );
+                    final Map<String, ErrorInformation> returnMap = new HashMap<>( fromJson );
+                    returnMap.keySet().retainAll( pwmApplication.getConfig().getLdapProfiles().keySet() );
+                    return returnMap;
+                }
             }
             }
         }
         }
         catch ( final Exception e )
         catch ( final Exception e )

+ 3 - 3
server/src/main/java/password/pwm/svc/httpclient/PwmHttpClient.java

@@ -535,10 +535,10 @@ public class PwmHttpClient implements AutoCloseable
                             final String name = headerElement.getName();
                             final String name = headerElement.getName();
                             if ( name != null )
                             if ( name != null )
                             {
                             {
-                                final HttpContentType httpContentType = HttpContentType.fromContentTypeHeader( name, null );
-                                if ( httpContentType != null )
+                                final Optional<HttpContentType> httpContentType = HttpContentType.fromContentTypeHeader( name, null );
+                                if ( httpContentType.isPresent() )
                                 {
                                 {
-                                    return Optional.of( httpContentType );
+                                    return httpContentType;
                                 }
                                 }
                             }
                             }
                         }
                         }

+ 2 - 2
server/src/main/java/password/pwm/svc/report/ReportService.java

@@ -680,8 +680,8 @@ public class ReportService implements PwmService
         {
         {
             try
             try
             {
             {
-                final ReportStatusInfo localReportStatus = pwmApplication.readAppAttribute( AppAttribute.REPORT_STATUS, ReportStatusInfo.class );
-                reportStatus.set( localReportStatus );
+                pwmApplication.readAppAttribute( AppAttribute.REPORT_STATUS, ReportStatusInfo.class )
+                        .ifPresent( reportStatus::set );
             }
             }
             catch ( final Exception e )
             catch ( final Exception e )
             {
             {

+ 3 - 7
server/src/main/java/password/pwm/svc/telemetry/TelemetryService.java

@@ -134,13 +134,9 @@ public class TelemetryService implements PwmService
             return;
             return;
         }
         }
 
 
-        {
-            final Instant storedLastPublishTimestamp = pwmApplication.readAppAttribute( AppAttribute.TELEMETRY_LAST_PUBLISH_TIMESTAMP, Instant.class );
-            lastPublishTime = storedLastPublishTimestamp != null
-                    ? storedLastPublishTimestamp
-                    : pwmApplication.getInstallTime();
-            LOGGER.trace( SessionLabel.TELEMETRY_SESSION_LABEL, () -> "last publish time was " + JavaHelper.toIsoDate( lastPublishTime ) );
-        }
+        lastPublishTime = pwmApplication.readAppAttribute( AppAttribute.TELEMETRY_LAST_PUBLISH_TIMESTAMP, Instant.class )
+                .orElse( pwmApplication.getInstallTime() );
+        LOGGER.trace( SessionLabel.TELEMETRY_SESSION_LABEL, () -> "last publish time was " + JavaHelper.toIsoDate( lastPublishTime ) );
 
 
         executorService = PwmScheduler.makeBackgroundExecutor( pwmApplication, TelemetryService.class );
         executorService = PwmScheduler.makeBackgroundExecutor( pwmApplication, TelemetryService.class );
 
 

+ 2 - 6
server/src/main/java/password/pwm/svc/wordlist/LocalDBWordlistBucket.java

@@ -119,12 +119,8 @@ class LocalDBWordlistBucket extends AbstractWordlistBucket implements WordlistBu
     public WordlistStatus readWordlistStatus()
     public WordlistStatus readWordlistStatus()
     {
     {
         final AppAttribute appAttribute = wordlistConfiguration.getMetaDataAppAttribute();
         final AppAttribute appAttribute = wordlistConfiguration.getMetaDataAppAttribute();
-        final WordlistStatus storedValue = pwmApplication.readAppAttribute( appAttribute, WordlistStatus.class );
-        if ( storedValue != null )
-        {
-            return storedValue;
-        }
-        return WordlistStatus.builder().build();
+        return pwmApplication.readAppAttribute( appAttribute, WordlistStatus.class )
+                .orElseGet( () -> WordlistStatus.builder().build() );
     }
     }
 
 
     @Override
     @Override

+ 1 - 3
server/src/main/java/password/pwm/util/CaptchaUtility.java

@@ -174,9 +174,7 @@ public class CaptchaUtility
             final String errorMsg = "unexpected error during reCaptcha API execution: " + e.getMessage();
             final String errorMsg = "unexpected error during reCaptcha API execution: " + e.getMessage();
             LOGGER.error( () -> errorMsg, e );
             LOGGER.error( () -> errorMsg, e );
             final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_CAPTCHA_API_ERROR, errorMsg );
             final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_CAPTCHA_API_ERROR, errorMsg );
-            final PwmUnrecoverableException pwmE = new PwmUnrecoverableException( errorInfo );
-            pwmE.initCause( e );
-            throw pwmE;
+            throw new PwmUnrecoverableException( errorInfo, e );
         }
         }
 
 
         LOGGER.trace( pwmRequest, () -> "captcha verification failed" );
         LOGGER.trace( pwmRequest, () -> "captcha verification failed" );

+ 0 - 1
server/src/main/java/password/pwm/util/OnejarHelper.java

@@ -118,7 +118,6 @@ public class OnejarHelper
                 .applicationMode( PwmApplicationMode.READ_ONLY )
                 .applicationMode( PwmApplicationMode.READ_ONLY )
                 .configurationFile( configFile )
                 .configurationFile( configFile )
                 .flags( Collections.singleton( PwmEnvironment.ApplicationFlag.CommandLineInstance ) )
                 .flags( Collections.singleton( PwmEnvironment.ApplicationFlag.CommandLineInstance ) )
-                .internalRuntimeInstance( true )
                 .build();
                 .build();
 
 
         return PwmApplication.createPwmApplication( pwmEnvironment );
         return PwmApplication.createPwmApplication( pwmEnvironment );

+ 6 - 5
server/src/main/java/password/pwm/util/db/DBConfiguration.java

@@ -27,6 +27,7 @@ import password.pwm.AppProperty;
 import password.pwm.config.Configuration;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.value.FileValue;
 import password.pwm.config.value.FileValue;
+import password.pwm.http.bean.ImmutableByteArray;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
@@ -46,16 +47,16 @@ public class DBConfiguration implements Serializable
     private final PasswordData password;
     private final PasswordData password;
     private final String columnTypeKey;
     private final String columnTypeKey;
     private final String columnTypeValue;
     private final String columnTypeValue;
-    private final byte[] jdbcDriver;
+    private final ImmutableByteArray jdbcDriver;
     private final Set<JDBCDriverLoader.ClassLoaderStrategy> classLoaderStrategies;
     private final Set<JDBCDriverLoader.ClassLoaderStrategy> classLoaderStrategies;
     private final int maxConnections;
     private final int maxConnections;
     private final int connectionTimeout;
     private final int connectionTimeout;
     private final int keyColumnLength;
     private final int keyColumnLength;
     private final boolean failOnIndexCreation;
     private final boolean failOnIndexCreation;
 
 
-    public byte[] getJdbcDriver( )
+    public ImmutableByteArray getJdbcDriver( )
     {
     {
-        return jdbcDriver == null ? null : Arrays.copyOf( jdbcDriver, jdbcDriver.length );
+        return jdbcDriver;
     }
     }
 
 
     public boolean isEnabled( )
     public boolean isEnabled( )
@@ -71,11 +72,11 @@ public class DBConfiguration implements Serializable
     {
     {
         final Map<FileValue.FileInformation, FileValue.FileContent> fileValue = config.readSettingAsFile(
         final Map<FileValue.FileInformation, FileValue.FileContent> fileValue = config.readSettingAsFile(
                 PwmSetting.DATABASE_JDBC_DRIVER );
                 PwmSetting.DATABASE_JDBC_DRIVER );
-        final byte[] jdbcDriverBytes;
+        final ImmutableByteArray jdbcDriverBytes;
         if ( fileValue != null && !fileValue.isEmpty() )
         if ( fileValue != null && !fileValue.isEmpty() )
         {
         {
             final FileValue.FileContent fileContent = fileValue.values().iterator().next();
             final FileValue.FileContent fileContent = fileValue.values().iterator().next();
-            jdbcDriverBytes = fileContent.getContents().copyOf();
+            jdbcDriverBytes = fileContent.getContents();
         }
         }
         else
         else
         {
         {

+ 13 - 12
server/src/main/java/password/pwm/util/db/JDBCDriverLoader.java

@@ -28,11 +28,12 @@ import password.pwm.PwmConstants;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.bean.ImmutableByteArray;
+import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
 import java.io.BufferedOutputStream;
 import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
 import java.io.File;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.IOException;
@@ -168,7 +169,7 @@ public class JDBCDriverLoader
                 throws DatabaseException
                 throws DatabaseException
         {
         {
             final String jdbcClassName = dbConfiguration.getDriverClassname();
             final String jdbcClassName = dbConfiguration.getDriverClassname();
-            final byte[] jdbcDriverBytes = dbConfiguration.getJdbcDriver();
+            final ImmutableByteArray jdbcDriverBytes = dbConfiguration.getJdbcDriver();
             try
             try
             {
             {
                 LOGGER.debug( () -> "loading JDBC database driver stored in configuration" );
                 LOGGER.debug( () -> "loading JDBC database driver stored in configuration" );
@@ -177,7 +178,7 @@ public class JDBCDriverLoader
                         ( PrivilegedAction<JarClassLoader> ) JarClassLoader::new
                         ( PrivilegedAction<JarClassLoader> ) JarClassLoader::new
                 );
                 );
 
 
-                jarClassLoader.add( new ByteArrayInputStream( jdbcDriverBytes ) );
+                jarClassLoader.add( jdbcDriverBytes.newByteArrayInputStream() );
                 final JclObjectFactory jclObjectFactory = JclObjectFactory.getInstance( true );
                 final JclObjectFactory jclObjectFactory = JclObjectFactory.getInstance( true );
 
 
                 //Create object of loaded class
                 //Create object of loaded class
@@ -217,9 +218,9 @@ public class JDBCDriverLoader
                 throws DatabaseException
                 throws DatabaseException
         {
         {
             final String jdbcClassName = dbConfiguration.getDriverClassname();
             final String jdbcClassName = dbConfiguration.getDriverClassname();
-            final byte[] jdbcDriverBytes = dbConfiguration.getJdbcDriver();
+            final ImmutableByteArray jdbcDriverBytes = dbConfiguration.getJdbcDriver();
 
 
-            if ( jdbcDriverBytes == null || jdbcDriverBytes.length < 1 )
+            if ( jdbcDriverBytes == null || jdbcDriverBytes.size() < 1 )
             {
             {
                 final String errorMsg = "jdbc driver file not configured, skipping";
                 final String errorMsg = "jdbc driver file not configured, skipping";
                 final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_DB_UNAVAILABLE, errorMsg );
                 final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_DB_UNAVAILABLE, errorMsg );
@@ -238,7 +239,7 @@ public class JDBCDriverLoader
 
 
                 try ( FileOutputStream fos = new FileOutputStream( tempFile ) )
                 try ( FileOutputStream fos = new FileOutputStream( tempFile ) )
                 {
                 {
-                    fos.write( jdbcDriverBytes );
+                    JavaHelper.copy( jdbcDriverBytes.newByteArrayInputStream(), fos );
                     fos.close();
                     fos.close();
                 }
                 }
 
 
@@ -297,9 +298,9 @@ public class JDBCDriverLoader
                 throws DatabaseException
                 throws DatabaseException
         {
         {
             final String jdbcClassName = dbConfiguration.getDriverClassname();
             final String jdbcClassName = dbConfiguration.getDriverClassname();
-            final byte[] jdbcDriverBytes = dbConfiguration.getJdbcDriver();
+            final ImmutableByteArray jdbcDriverBytes = dbConfiguration.getJdbcDriver();
 
 
-            if ( jdbcDriverBytes == null || jdbcDriverBytes.length < 1 )
+            if ( jdbcDriverBytes == null || jdbcDriverBytes.size() < 1 )
             {
             {
                 final String errorMsg = "jdbc driver file not configured, skipping";
                 final String errorMsg = "jdbc driver file not configured, skipping";
                 final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_DB_UNAVAILABLE, errorMsg );
                 final ErrorInformation errorInformation = new ErrorInformation( PwmError.ERROR_DB_UNAVAILABLE, errorMsg );
@@ -309,7 +310,7 @@ public class JDBCDriverLoader
             final String jdbcDriverHash;
             final String jdbcDriverHash;
             try
             try
             {
             {
-                jdbcDriverHash = pwmApplication.getSecureService().hash( jdbcDriverBytes );
+                jdbcDriverHash = pwmApplication.getSecureService().hash( jdbcDriverBytes.newByteArrayInputStream() );
             }
             }
             catch ( final PwmUnrecoverableException e )
             catch ( final PwmUnrecoverableException e )
             {
             {
@@ -366,10 +367,10 @@ public class JDBCDriverLoader
         {
         {
         }
         }
 
 
-        File createOrGetTempJarFile( final PwmApplication pwmApplication, final byte[] jarBytes ) throws PwmUnrecoverableException, IOException
+        File createOrGetTempJarFile( final PwmApplication pwmApplication, final ImmutableByteArray jarBytes ) throws PwmUnrecoverableException, IOException
         {
         {
             final File file = pwmApplication.getTempDirectory();
             final File file = pwmApplication.getTempDirectory();
-            final String jarHash = pwmApplication.getSecureService().hash( jarBytes );
+            final String jarHash = pwmApplication.getSecureService().hash( jarBytes.newByteArrayInputStream() );
             final String tempFileName = "jar-" + jarHash + ".jar";
             final String tempFileName = "jar-" + jarHash + ".jar";
             final File tempFile = new File( file.getAbsolutePath() + File.separator + tempFileName );
             final File tempFile = new File( file.getAbsolutePath() + File.separator + tempFileName );
             if ( tempFile.exists() )
             if ( tempFile.exists() )
@@ -388,7 +389,7 @@ public class JDBCDriverLoader
             {
             {
                 LOGGER.debug( () -> "creating temp jar file " + tempFile.getAbsolutePath() );
                 LOGGER.debug( () -> "creating temp jar file " + tempFile.getAbsolutePath() );
                 final OutputStream fos = new BufferedOutputStream( new FileOutputStream( tempFile ) );
                 final OutputStream fos = new BufferedOutputStream( new FileOutputStream( tempFile ) );
-                fos.write( jarBytes );
+                JavaHelper.copy( jarBytes.newByteArrayInputStream(), fos );
                 fos.close();
                 fos.close();
             }
             }
             else
             else

+ 39 - 1
server/src/main/java/password/pwm/util/java/JavaHelper.java

@@ -53,6 +53,7 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.Comparator;
 import java.util.Date;
 import java.util.Date;
+import java.util.EnumMap;
 import java.util.EnumSet;
 import java.util.EnumSet;
 import java.util.Enumeration;
 import java.util.Enumeration;
 import java.util.LinkedHashSet;
 import java.util.LinkedHashSet;
@@ -102,7 +103,7 @@ public class JavaHelper
                         "D",
                         "D",
                         "E",
                         "E",
                         "F",
                         "F",
-                };
+                        };
 
 
         if ( in == null || in.length <= 0 )
         if ( in == null || in.length <= 0 )
         {
         {
@@ -677,4 +678,41 @@ public class JavaHelper
                 ? EnumSet.noneOf( classOfT )
                 ? EnumSet.noneOf( classOfT )
                 : EnumSet.copyOf( source );
                 : EnumSet.copyOf( source );
     }
     }
+
+    public static String requireNonEmpty( final String input )
+    {
+        if ( StringUtil.isEmpty( input ) )
+        {
+            throw new NullPointerException( );
+        }
+        return input;
+    }
+
+    public static String requireNonEmpty( final String input, final String message )
+    {
+        if ( StringUtil.isEmpty( input ) )
+        {
+            throw new NullPointerException( message );
+        }
+        return input;
+    }
+
+    public static <K extends Enum<K>, V> EnumMap<K, V> copiedEnumMap( final Map<K, V> source, final Class<K> classOfT )
+    {
+        if ( source == null )
+        {
+            return new EnumMap<K, V>( classOfT );
+        }
+
+        final EnumMap<K, V> returnMap = new EnumMap<>( classOfT );
+        for ( final Map.Entry<K, V> entry : source.entrySet() )
+        {
+            final K key = entry.getKey();
+            if ( key != null )
+            {
+                returnMap.put( key, entry.getValue() );
+            }
+        }
+        return returnMap;
+    }
 }
 }

+ 6 - 0
server/src/main/java/password/pwm/util/localdb/LocalDBFactory.java

@@ -104,6 +104,12 @@ public class LocalDBFactory
 
 
             final StringBuilder debugText = new StringBuilder();
             final StringBuilder debugText = new StringBuilder();
             debugText.append( "LocalDB open" );
             debugText.append( "LocalDB open" );
+
+            if ( readonly )
+            {
+                debugText.append( " (read-only)" );
+            }
+
             if ( localDB.getFileLocation() != null )
             if ( localDB.getFileLocation() != null )
             {
             {
                 debugText.append( ", db size: " ).append( StringUtil.formatDiskSize( FileSystemUtility.getFileDirectorySize( localDB.getFileLocation() ) ) );
                 debugText.append( ", db size: " ).append( StringUtil.formatDiskSize( FileSystemUtility.getFileDirectorySize( localDB.getFileLocation() ) ) );

+ 12 - 9
server/src/main/java/password/pwm/util/logging/LocalDBLogger.java

@@ -108,15 +108,18 @@ public class LocalDBLogger implements PwmService
 
 
         if ( pwmApplication != null )
         if ( pwmApplication != null )
         {
         {
-            final String currentFormat = pwmApplication.readAppAttribute( AppAttribute.LOCALDB_LOGGER_STORAGE_FORMAT, String.class );
-            if ( !STORAGE_FORMAT_VERSION.equals( currentFormat ) )
-            {
-                LOGGER.warn( () -> "localdb logger is using outdated format, clearing existing records (existing='"
-                        + currentFormat + "', current='" + STORAGE_FORMAT_VERSION + "')" );
-
-                localDBListQueue.clear();
-                pwmApplication.writeAppAttribute( AppAttribute.LOCALDB_LOGGER_STORAGE_FORMAT, STORAGE_FORMAT_VERSION );
-            }
+            pwmApplication.readAppAttribute( AppAttribute.LOCALDB_LOGGER_STORAGE_FORMAT, String.class )
+                    .ifPresent( ( currentFormat ) ->
+                    {
+                        if ( !STORAGE_FORMAT_VERSION.equals( currentFormat ) )
+                        {
+                            LOGGER.warn( () -> "localdb logger is using outdated format, clearing existing records (existing='"
+                                    + currentFormat + "', current='" + STORAGE_FORMAT_VERSION + "')" );
+
+                            localDBListQueue.clear();
+                            pwmApplication.writeAppAttribute( AppAttribute.LOCALDB_LOGGER_STORAGE_FORMAT, STORAGE_FORMAT_VERSION );
+                        }
+                    } );
         }
         }
 
 
         status = STATUS.OPEN;
         status = STATUS.OPEN;

+ 1 - 3
server/src/main/java/password/pwm/util/operations/cr/LocalDbCrOperator.java

@@ -132,9 +132,7 @@ public class LocalDbCrOperator implements CrOperator
         catch ( final LocalDBException e )
         catch ( final LocalDBException e )
         {
         {
             final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_CLEARING_RESPONSES, "unexpected LocalDB error clearing responses: " + e.getMessage() );
             final ErrorInformation errorInfo = new ErrorInformation( PwmError.ERROR_CLEARING_RESPONSES, "unexpected LocalDB error clearing responses: " + e.getMessage() );
-            final PwmUnrecoverableException pwmOE = new PwmUnrecoverableException( errorInfo );
-            pwmOE.initCause( e );
-            throw pwmOE;
+            throw new PwmUnrecoverableException( errorInfo, e );
         }
         }
     }
     }
 
 

+ 1 - 3
server/src/main/java/password/pwm/util/password/PasswordUtility.java

@@ -734,9 +734,7 @@ public class PasswordUtility
                     PwmError.ERROR_SERVICE_UNREACHABLE,
                     PwmError.ERROR_SERVICE_UNREACHABLE,
                     e.getErrorInformation().getDetailedErrorMsg(), e.getErrorInformation().getFieldValues()
                     e.getErrorInformation().getDetailedErrorMsg(), e.getErrorInformation().getFieldValues()
             );
             );
-            final PwmUnrecoverableException newException = new PwmUnrecoverableException( info );
-            newException.initCause( e );
-            throw newException;
+            throw new PwmUnrecoverableException( info, e );
         }
         }
     }
     }
 
 

+ 8 - 0
server/src/main/java/password/pwm/util/secure/SecureService.java

@@ -205,6 +205,14 @@ public class SecureService implements PwmService
         return SecureEngine.hash( input, defaultHashAlgorithm );
         return SecureEngine.hash( input, defaultHashAlgorithm );
     }
     }
 
 
+    public String hash(
+            final InputStream input
+    )
+            throws PwmUnrecoverableException
+    {
+        return SecureEngine.hash( input, defaultHashAlgorithm );
+    }
+
     public String hash(
     public String hash(
             final File file
             final File file
     )
     )

+ 1 - 2
server/src/main/java/password/pwm/util/secure/self/SelfCertFactory.java

@@ -82,8 +82,7 @@ public class SelfCertFactory
 
 
     private static Optional<StoredCertData> loadExistingStoredCert( final PwmApplication pwmApplication )
     private static Optional<StoredCertData> loadExistingStoredCert( final PwmApplication pwmApplication )
     {
     {
-        final StoredCertData storedCertData = pwmApplication.readAppAttribute( AppAttribute.HTTPS_SELF_CERT, StoredCertData.class );
-        return Optional.ofNullable( storedCertData );
+        return pwmApplication.readAppAttribute( AppAttribute.HTTPS_SELF_CERT, StoredCertData.class );
     }
     }
 
 
     private static boolean evaluateExistingStoredCert( final StoredCertData storedCertData, final Settings settings )
     private static boolean evaluateExistingStoredCert( final StoredCertData storedCertData, final Settings settings )

+ 4 - 4
server/src/main/java/password/pwm/ws/server/RestRequest.java

@@ -36,6 +36,7 @@ import password.pwm.util.logging.PwmLogger;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequest;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
+import java.util.Optional;
 
 
 public class RestRequest extends PwmHttpRequestWrapper
 public class RestRequest extends PwmHttpRequestWrapper
 {
 {
@@ -81,18 +82,17 @@ public class RestRequest extends PwmHttpRequestWrapper
         return pwmApplication;
         return pwmApplication;
     }
     }
 
 
-    public HttpContentType readContentType( )
+    public Optional<HttpContentType> readContentType( )
     {
     {
         return HttpContentType.fromContentTypeHeader( readHeaderValueAsString( HttpHeader.ContentType ), null );
         return HttpContentType.fromContentTypeHeader( readHeaderValueAsString( HttpHeader.ContentType ), null );
     }
     }
 
 
-    public HttpContentType readAcceptType( )
+    public Optional<HttpContentType> readAcceptType( )
     {
     {
-
         return readAcceptType( getHttpServletRequest() );
         return readAcceptType( getHttpServletRequest() );
     }
     }
 
 
-    static HttpContentType readAcceptType( final HttpServletRequest request )
+    static Optional<HttpContentType> readAcceptType( final HttpServletRequest request )
     {
     {
         final String acceptHeaderValue = request.getHeader( HttpHeader.Accept.getHttpName() );
         final String acceptHeaderValue = request.getHeader( HttpHeader.Accept.getHttpName() );
         return HttpContentType.fromContentTypeHeader( acceptHeaderValue, HttpContentType.json );
         return HttpContentType.fromContentTypeHeader( acceptHeaderValue, HttpContentType.json );

+ 35 - 15
server/src/main/java/password/pwm/ws/server/RestServlet.java

@@ -63,6 +63,7 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.List;
 import java.util.List;
 import java.util.Locale;
 import java.util.Locale;
+import java.util.Optional;
 
 
 public abstract class RestServlet extends HttpServlet
 public abstract class RestServlet extends HttpServlet
 {
 {
@@ -225,8 +226,8 @@ public abstract class RestServlet extends HttpServlet
             throws PwmUnrecoverableException, IOException
             throws PwmUnrecoverableException, IOException
     {
     {
         final HttpMethod reqMethod = restRequest.getMethod();
         final HttpMethod reqMethod = restRequest.getMethod();
-        final HttpContentType reqContent = restRequest.readContentType();
-        final HttpContentType reqAccept = restRequest.readAcceptType();
+        final Optional<HttpContentType> reqContent = restRequest.readContentType();
+        final Optional<HttpContentType> reqAccept = restRequest.readAcceptType();
 
 
         final boolean careAboutContentType = reqMethod.isHasBody();
         final boolean careAboutContentType = reqMethod.isHasBody();
 
 
@@ -240,25 +241,43 @@ public abstract class RestServlet extends HttpServlet
 
 
             if ( annotation != null )
             if ( annotation != null )
             {
             {
-                if ( annotation.method().length == 0 || Arrays.asList( annotation.method() ).contains( reqMethod ) )
+                if (
+                        annotation.method().length == 0
+                                || Arrays.asList( annotation.method() ).contains( reqMethod )
+                )
                 {
                 {
                     loopMatch.setMethodMatch( true );
                     loopMatch.setMethodMatch( true );
                     anyMatch.setMethodMatch( true );
                     anyMatch.setMethodMatch( true );
                 }
                 }
 
 
-                if ( !careAboutContentType || annotation.consumes().length == 0 || Arrays.asList( annotation.consumes() ).contains( reqContent ) )
+                if ( reqContent.isPresent() )
                 {
                 {
-                    loopMatch.setContentMatch( true );
-                    anyMatch.setContentMatch( true );
+                    if (
+                            !careAboutContentType
+                                    || annotation.consumes().length == 0
+                                    || Arrays.asList( annotation.consumes() ).contains( reqContent.get() ) )
+                    {
+                        loopMatch.setContentMatch( true );
+                        anyMatch.setContentMatch( true );
+                    }
                 }
                 }
 
 
-                if ( annotation.produces().length == 0 || Arrays.asList( annotation.produces() ).contains( reqAccept ) )
+                if ( reqAccept.isPresent() )
                 {
                 {
-                    loopMatch.setAcceptMatch( true );
-                    anyMatch.setAcceptMatch( true );
+                    if (
+                            annotation.produces().length == 0
+                                    || Arrays.asList( annotation.produces() ).contains( reqAccept.get() )
+                    )
+                    {
+                        loopMatch.setAcceptMatch( true );
+                        anyMatch.setAcceptMatch( true );
+                    }
                 }
                 }
 
 
-                if ( loopMatch.isMethodMatch() && loopMatch.isContentMatch() && loopMatch.isAcceptMatch() )
+                if (
+                        loopMatch.isMethodMatch()
+                                && loopMatch.isContentMatch()
+                                && loopMatch.isAcceptMatch() )
                 {
                 {
                     return method;
                     return method;
                 }
                 }
@@ -270,11 +289,11 @@ public abstract class RestServlet extends HttpServlet
         {
         {
             errorMsg = "HTTP method unavailable";
             errorMsg = "HTTP method unavailable";
         }
         }
-        else if ( reqAccept == null && !anyMatch.isAcceptMatch() )
+        else if ( !reqAccept.isPresent() && !anyMatch.isAcceptMatch() )
         {
         {
             errorMsg = HttpHeader.Accept.getHttpName() + " header is required";
             errorMsg = HttpHeader.Accept.getHttpName() + " header is required";
         }
         }
-        else if ( reqContent == null && !anyMatch.isContentMatch() )
+        else if ( !reqContent.isPresent() && !anyMatch.isContentMatch() )
         {
         {
             errorMsg = HttpHeader.ContentType.getHttpName() + " header is required";
             errorMsg = HttpHeader.ContentType.getHttpName() + " header is required";
         }
         }
@@ -336,7 +355,7 @@ public abstract class RestServlet extends HttpServlet
 
 
         if ( restRequest.getMethod().isHasBody() )
         if ( restRequest.getMethod().isHasBody() )
         {
         {
-            if ( restRequest.readContentType() == null )
+            if ( !restRequest.readContentType().isPresent() )
             {
             {
                 final String message = restRequest.getMethod() + " method requires " + HttpHeader.ContentType.getHttpName() + " header";
                 final String message = restRequest.getMethod() + " method requires " + HttpHeader.ContentType.getHttpName() + " header";
                 throw PwmUnrecoverableException.newException( PwmError.ERROR_UNAUTHORIZED, message );
                 throw PwmUnrecoverableException.newException( PwmError.ERROR_UNAUTHORIZED, message );
@@ -353,11 +372,12 @@ public abstract class RestServlet extends HttpServlet
     )
     )
             throws IOException
             throws IOException
     {
     {
-        final HttpContentType acceptType = RestRequest.readAcceptType( request );
         resp.setHeader( HttpHeader.Server.getHttpName(), PwmConstants.PWM_APP_NAME );
         resp.setHeader( HttpHeader.Server.getHttpName(), PwmConstants.PWM_APP_NAME );
 
 
-        if ( acceptType != null )
+        final Optional<HttpContentType> optionalHttpContentType = RestRequest.readAcceptType( request );
+        if ( optionalHttpContentType.isPresent() )
         {
         {
+            final HttpContentType acceptType = optionalHttpContentType.get();
             switch ( acceptType )
             switch ( acceptType )
             {
             {
                 case json:
                 case json: