Explorar el Código

add spotbugs to pom and fix existing detected issues

Jason Rivard hace 7 años
padre
commit
6fb5f22e19
Se han modificado 100 ficheros con 735 adiciones y 848 borrados
  1. 1 1
      client/pom.xml
  2. 2 1
      pom.xml
  3. 49 4
      server/pom.xml
  4. 6 1
      server/src/build/checkstyle-import.xml
  5. 6 0
      server/src/build/spotbugs-exclude.xml
  6. 0 11
      server/src/main/java/password/pwm/Permission.java
  7. 12 8
      server/src/main/java/password/pwm/PwmApplication.java
  8. 4 12
      server/src/main/java/password/pwm/PwmConstants.java
  9. 3 5
      server/src/main/java/password/pwm/PwmEnvironment.java
  10. 16 6
      server/src/main/java/password/pwm/bean/PrivateKeyCertificate.java
  11. 32 32
      server/src/main/java/password/pwm/config/Configuration.java
  12. 70 66
      server/src/main/java/password/pwm/config/PwmSetting.java
  13. 1 1
      server/src/main/java/password/pwm/config/SettingReader.java
  14. 12 2
      server/src/main/java/password/pwm/config/function/AbstractUriCertImportFunction.java
  15. 10 1
      server/src/main/java/password/pwm/config/function/ActionCertImportFunction.java
  16. 2 3
      server/src/main/java/password/pwm/config/function/LdapCertImportFunction.java
  17. 1 1
      server/src/main/java/password/pwm/config/function/RemoteWebServiceCertImportFunction.java
  18. 3 3
      server/src/main/java/password/pwm/config/function/SyslogCertImportFunction.java
  19. 5 4
      server/src/main/java/password/pwm/config/option/IdentityVerificationMethod.java
  20. 6 3
      server/src/main/java/password/pwm/config/profile/AbstractProfile.java
  21. 3 2
      server/src/main/java/password/pwm/config/profile/ChallengeProfile.java
  22. 3 2
      server/src/main/java/password/pwm/config/profile/LdapProfile.java
  23. 2 4
      server/src/main/java/password/pwm/config/profile/ProfileUtility.java
  24. 3 8
      server/src/main/java/password/pwm/config/profile/PwmPasswordPolicy.java
  25. 5 6
      server/src/main/java/password/pwm/config/stored/ConfigChangeLogImpl.java
  26. 15 11
      server/src/main/java/password/pwm/config/stored/ConfigurationReader.java
  27. 4 10
      server/src/main/java/password/pwm/config/stored/NGStoredConfigurationFactory.java
  28. 3 1
      server/src/main/java/password/pwm/config/stored/StoredConfigReferenceBean.java
  29. 17 17
      server/src/main/java/password/pwm/config/stored/StoredConfigurationImpl.java
  30. 9 6
      server/src/main/java/password/pwm/config/value/ChallengeValue.java
  31. 12 99
      server/src/main/java/password/pwm/config/value/EmailValue.java
  32. 22 23
      server/src/main/java/password/pwm/config/value/FileValue.java
  33. 8 6
      server/src/main/java/password/pwm/config/value/LocalizedStringArrayValue.java
  34. 10 9
      server/src/main/java/password/pwm/config/value/LocalizedStringValue.java
  35. 9 7
      server/src/main/java/password/pwm/config/value/NamedSecretValue.java
  36. 1 1
      server/src/main/java/password/pwm/config/value/NumericValue.java
  37. 1 2
      server/src/main/java/password/pwm/config/value/PrivateKeyValue.java
  38. 1 3
      server/src/main/java/password/pwm/config/value/ValueFactory.java
  39. 4 10
      server/src/main/java/password/pwm/config/value/VerificationMethodValue.java
  40. 3 2
      server/src/main/java/password/pwm/config/value/X509CertificateValue.java
  41. 3 2
      server/src/main/java/password/pwm/config/value/data/ActionConfiguration.java
  42. 3 5
      server/src/main/java/password/pwm/config/value/data/FormConfiguration.java
  43. 2 1
      server/src/main/java/password/pwm/config/value/data/RemoteWebServiceConfiguration.java
  44. 1 7
      server/src/main/java/password/pwm/error/ErrorInformation.java
  45. 0 2
      server/src/main/java/password/pwm/error/PwmDataValidationException.java
  46. 0 2
      server/src/main/java/password/pwm/error/PwmException.java
  47. 3 3
      server/src/main/java/password/pwm/health/ApplianceStatusChecker.java
  48. 9 4
      server/src/main/java/password/pwm/health/CertificateChecker.java
  49. 5 1
      server/src/main/java/password/pwm/health/HealthRecord.java
  50. 7 5
      server/src/main/java/password/pwm/health/LDAPStatusChecker.java
  51. 4 14
      server/src/main/java/password/pwm/http/ContextManager.java
  52. 4 3
      server/src/main/java/password/pwm/http/HttpContentType.java
  53. 2 0
      server/src/main/java/password/pwm/http/IdleTimeoutCalculator.java
  54. 9 7
      server/src/main/java/password/pwm/http/PwmHttpRequestWrapper.java
  55. 18 41
      server/src/main/java/password/pwm/http/PwmRequest.java
  56. 2 0
      server/src/main/java/password/pwm/http/PwmResponse.java
  57. 17 14
      server/src/main/java/password/pwm/http/PwmSession.java
  58. 2 3
      server/src/main/java/password/pwm/http/PwmURL.java
  59. 3 4
      server/src/main/java/password/pwm/http/SessionManager.java
  60. 0 3
      server/src/main/java/password/pwm/http/bean/ChangePasswordBean.java
  61. 2 1
      server/src/main/java/password/pwm/http/bean/ConfigGuideBean.java
  62. 1 1
      server/src/main/java/password/pwm/http/bean/ConfigManagerBean.java
  63. 0 1
      server/src/main/java/password/pwm/http/bean/ForgottenPasswordBean.java
  64. 4 7
      server/src/main/java/password/pwm/http/bean/GuestRegistrationBean.java
  65. 16 0
      server/src/main/java/password/pwm/http/bean/ImmutableByteArray.java
  66. 10 7
      server/src/main/java/password/pwm/http/client/PwmHttpClient.java
  67. 7 33
      server/src/main/java/password/pwm/http/client/PwmHttpClientConfiguration.java
  68. 6 5
      server/src/main/java/password/pwm/http/client/PwmHttpClientRequest.java
  69. 0 1
      server/src/main/java/password/pwm/http/filter/AuthenticationFilter.java
  70. 0 6
      server/src/main/java/password/pwm/http/filter/AuthorizationFilter.java
  71. 10 10
      server/src/main/java/password/pwm/http/filter/ConfigAccessFilter.java
  72. 0 2
      server/src/main/java/password/pwm/http/filter/GZIPFilter.java
  73. 30 30
      server/src/main/java/password/pwm/http/filter/RequestInitializationFilter.java
  74. 3 3
      server/src/main/java/password/pwm/http/servlet/ActivateUserServlet.java
  75. 13 12
      server/src/main/java/password/pwm/http/servlet/GuestRegistrationServlet.java
  76. 2 1
      server/src/main/java/password/pwm/http/servlet/PwmServletDefinition.java
  77. 5 24
      server/src/main/java/password/pwm/http/servlet/SetupResponsesServlet.java
  78. 3 2
      server/src/main/java/password/pwm/http/servlet/ShortcutServlet.java
  79. 0 1
      server/src/main/java/password/pwm/http/servlet/UpdateProfileServlet.java
  80. 1 2
      server/src/main/java/password/pwm/http/servlet/admin/AppDashboardData.java
  81. 2 2
      server/src/main/java/password/pwm/http/servlet/admin/ReportStatusBean.java
  82. 0 7
      server/src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServlet.java
  83. 3 2
      server/src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServletUtil.java
  84. 2 6
      server/src/main/java/password/pwm/http/servlet/configeditor/ConfigEditorServlet.java
  85. 2 2
      server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java
  86. 24 70
      server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerCertificatesServlet.java
  87. 13 16
      server/src/main/java/password/pwm/http/servlet/configmanager/DebugItemGenerator.java
  88. 58 41
      server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java
  89. 3 2
      server/src/main/java/password/pwm/http/servlet/newuser/NewUserForm.java
  90. 6 3
      server/src/main/java/password/pwm/http/servlet/oauth/OAuthMachine.java
  91. 3 2
      server/src/main/java/password/pwm/http/servlet/oauth/OAuthSettings.java
  92. 3 2
      server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchDataReader.java
  93. 3 6
      server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchResourcesServlet.java
  94. 5 3
      server/src/main/java/password/pwm/http/servlet/resource/CacheEntry.java
  95. 6 4
      server/src/main/java/password/pwm/http/servlet/resource/MemoryFileResource.java
  96. 7 5
      server/src/main/java/password/pwm/http/servlet/resource/ResourceFileServlet.java
  97. 7 5
      server/src/main/java/password/pwm/http/servlet/resource/ResourceServletConfiguration.java
  98. 1 2
      server/src/main/java/password/pwm/http/servlet/resource/ResourceServletService.java
  99. 4 3
      server/src/main/java/password/pwm/http/state/CryptoCookieBeanImpl.java
  100. 0 1
      server/src/main/java/password/pwm/http/state/CryptoRequestBeanImpl.java

+ 1 - 1
client/pom.xml

@@ -29,7 +29,7 @@
                         <configuration>
                             <rules>
                                 <requireMavenVersion>
-                                    <version>3.2</version>
+                                    <version>${pwm.minimum.maven.version}</version>
                                 </requireMavenVersion>
                             </rules>
                         </configuration>

+ 2 - 1
pom.xml

@@ -26,6 +26,7 @@
         <build.number>0</build.number>  <!-- default in case not set on command line -->
         <build.revision>0</build.revision>  <!-- default in case not set on command line -->
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+        <pwm.minimum.maven.version>3.2</pwm.minimum.maven.version>
     </properties>
 
     <modules>
@@ -48,7 +49,7 @@
                         <configuration>
                             <rules>
                                 <requireMavenVersion>
-                                    <version>3.2</version>
+                                    <version>${pwm.minimum.maven.version}</version>
                                 </requireMavenVersion>
                             </rules>
                         </configuration>

+ 49 - 4
server/pom.xml

@@ -18,6 +18,7 @@
         <maven.compiler.source>1.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
         <skipTests>false</skipTests>
+        <skipSpotbugs>false</skipSpotbugs>
         <timestamp.iso>${maven.build.timestamp}</timestamp.iso>
         <maven.build.timestamp.format>yyyy-MM-dd'T'HH:mm:ss'Z'</maven.build.timestamp.format>
     </properties>
@@ -47,6 +48,12 @@
                 <jspc.skip>true</jspc.skip>
             </properties>
         </profile>
+        <profile>
+            <id>skip-spotbugs</id>
+            <properties>
+                <skipSpotbugs>true</skipSpotbugs>
+            </properties>
+        </profile>
         <profile>
             <id>doclint-java8-disable</id>
             <activation>
@@ -88,6 +95,38 @@
 
     <build>
         <plugins>
+            <plugin>
+                <groupId>com.github.hazendaz.spotbugs</groupId>
+                <artifactId>spotbugs-maven-plugin</artifactId>
+                <version>3.1.0-RC4</version>
+                <dependencies>
+                    <dependency>
+                        <groupId>com.github.spotbugs</groupId>
+                        <artifactId>spotbugs</artifactId>
+                        <version>3.1.0-RC7</version>
+                    </dependency>
+                </dependencies>
+                <configuration>
+                    <excludeFilterFile>src/build/spotbugs-exclude.xml</excludeFilterFile>
+                    <includeTests>false</includeTests>
+                    <skip>${skipSpotbugs}</skip>
+                </configuration>
+                <executions>
+                    <execution>
+                        <phase>compile</phase>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                    <execution>
+                        <id>findbugs-test-compile</id>
+                        <phase>test-compile</phase>
+                        <goals>
+                            <goal>check</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-enforcer-plugin</artifactId>
@@ -101,7 +140,7 @@
                         <configuration>
                             <rules>
                                 <requireMavenVersion>
-                                    <version>3.2</version>
+                                    <version>${pwm.minimum.maven.version}</version>
                                 </requireMavenVersion>
                             </rules>
                         </configuration>
@@ -520,9 +559,9 @@
          -->
         <plugins>
             <plugin>
-                <groupId>org.codehaus.mojo</groupId>
-                <artifactId>findbugs-maven-plugin</artifactId>
-                <version>3.0.4</version>
+                <groupId>com.github.hazendaz.spotbugs</groupId>
+                <artifactId>spotbugs-maven-plugin</artifactId>
+                <version>3.1.0-RC4</version>
             </plugin>
             <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
@@ -540,6 +579,12 @@
             <version>1.16.18</version>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>com.github.spotbugs</groupId>
+            <artifactId>spotbugs-annotations</artifactId>
+            <version>3.1.0-RC7</version>
+            <scope>provided</scope>
+        </dependency>
 
         <!-- Test dependencies -->
         <dependency>

+ 6 - 1
server/src/build/checkstyle-import.xml

@@ -67,6 +67,7 @@
     <allow pkg="com.novell.ldap"/>
     <allow pkg="java.security"/>
     <allow pkg="javax.swing"/>
+    <allow pkg="javax.annotation"/>
     <allow pkg="java.awt"/>
     <allow pkg="javax.security"/>
     <allow pkg="eu.bitwalker.useragentutils"/>
@@ -80,7 +81,11 @@
     <allow pkg="lombok"/>
     <allow pkg="com.github.benmanes.caffeine"/>
 
-    <allow pkg="sun.management"/>
+    <allow pkg="edu.umd.cs.findbugs.annotations"/>
+
+    <subpackage name="password.pwm.util">
+        <allow pkg="sun.management"/>
+    </subpackage>
 
 
     <!--servlet -->

+ 6 - 0
server/src/build/spotbugs-exclude.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<FindBugsFilter>
+    <Match>
+        <Bug pattern="SE_NO_SERIALVERSIONID,IC_INIT_CIRCULARITY,RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE,DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED,SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING"/>
+    </Match>
+</FindBugsFilter>

+ 0 - 11
server/src/main/java/password/pwm/Permission.java

@@ -43,31 +43,20 @@ public enum Permission {
     ;
 
 
-// ------------------------------ FIELDS ------------------------------
-
     private static final PwmLogger LOGGER = PwmLogger.forClass(Permission.class);
 
     private PwmSetting pwmSetting;
 
-// -------------------------- STATIC METHODS --------------------------
-
-
-// --------------------------- CONSTRUCTORS ---------------------------
-
     Permission(final PwmSetting pwmSetting)
     {
         this.pwmSetting = pwmSetting;
     }
 
-// --------------------- GETTER / SETTER METHODS ---------------------
-
     public PwmSetting getPwmSetting()
     {
         return pwmSetting;
     }
 
-// -------------------------- ENUMERATIONS --------------------------
-
     public enum PermissionStatus {
         UNCHECKED,
         GRANTED,

+ 12 - 8
server/src/main/java/password/pwm/PwmApplication.java

@@ -103,9 +103,7 @@ import java.util.Map;
  * @author Jason D. Rivard
  */
 public class PwmApplication {
-// ------------------------------ FIELDS ------------------------------
 
-    // ----------------------------- CONSTANTS ----------------------------
     private static final PwmLogger LOGGER = PwmLogger.forClass(PwmApplication.class);
     private static final String DEFAULT_INSTANCE_ID = "-1";
 
@@ -308,8 +306,8 @@ public class PwmApplication {
             final Map<AppProperty,String> nonDefaultProperties = getConfig().readAllNonDefaultAppProperties();
             if (nonDefaultProperties != null && !nonDefaultProperties.isEmpty()) {
                 final Map<String,String> tempMap = new LinkedHashMap<>();
-                for (final AppProperty loopProperty : nonDefaultProperties.keySet()) {
-                    tempMap.put(loopProperty.getKey(), nonDefaultProperties.get(loopProperty));
+                for (final Map.Entry<AppProperty,String> entry : nonDefaultProperties.entrySet()) {
+                    tempMap.put(entry.getKey().getKey(), entry.getValue());
                 }
                 LOGGER.trace("non-default app properties read from configuration: " + JsonUtil.serializeMap(tempMap));
             } else {
@@ -376,7 +374,11 @@ public class PwmApplication {
                     LOGGER.trace("deleted existing keystore file: " + keyStoreFile.getAbsolutePath());
                 }
             }
-            new FileOutputStream(keyStoreFile).write(outputContents.toByteArray());
+
+            try (FileOutputStream fileOutputStream = new FileOutputStream(keyStoreFile)) {
+                fileOutputStream.write(outputContents.toByteArray());
+            }
+
             LOGGER.info("successfully exported application https key to keystore file " + keyStoreFile.getAbsolutePath());
         }
     }
@@ -414,7 +416,11 @@ public class PwmApplication {
                     LOGGER.trace("deleted existing tomcat configuration file: " + tomcatOutputFile.getAbsolutePath());
                 }
             }
-            new FileOutputStream(tomcatOutputFile).write(outputContents.toByteArray());
+
+            try (FileOutputStream fileOutputStream = new FileOutputStream(tomcatOutputFile)) {
+                fileOutputStream.write(outputContents.toByteArray());
+            }
+
             LOGGER.info("successfully wrote tomcat configuration to file " + tomcatOutputFile.getAbsolutePath());
         }
     }
@@ -691,8 +697,6 @@ public class PwmApplication {
         return localDB;
     }
 
-// -------------------------- INNER CLASSES --------------------------
-
     private static class Initializer {
 
         public static LocalDB initializeLocalDB(final PwmApplication pwmApplication) throws PwmUnrecoverableException {

+ 4 - 12
server/src/main/java/password/pwm/PwmConstants.java

@@ -24,12 +24,12 @@ package password.pwm;
 
 import org.apache.commons.csv.CSVFormat;
 import password.pwm.util.java.JsonUtil;
-import password.pwm.util.secure.PwmBlockAlgorithm;
 import password.pwm.util.secure.PwmHashAlgorithm;
 
 import java.nio.charset.Charset;
 import java.time.Instant;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -43,9 +43,7 @@ import java.util.TimeZone;
  * @author Jason D. Rivard
  */
 public abstract class PwmConstants {
-// ------------------------------ FIELDS ------------------------------
 
-    // ------------------------- PUBLIC CONSTANTS -------------------------
     public static final String BUILD_TIME           = readBuildInfoBundle("build.time", Instant.now().toString());
     public static final String BUILD_NUMBER         = readBuildInfoBundle("build.number","0");
     public static final String BUILD_TYPE           = readBuildInfoBundle("build.type","");
@@ -102,21 +100,18 @@ public abstract class PwmConstants {
     public static final String PROFILE_ID_ALL = "all";
 
     public static final String TOKEN_KEY_PWD_CHG_DATE = "_lastPwdChange";
-    public static final float JAVA_MINIMUM_VERSION = (float)1.6;
 
     public static final String HTTP_BASIC_AUTH_PREFIX = readPwmConstantsBundle("httpHeaderAuthorizationBasic");
 
     public static final String DEFAULT_BAD_PASSWORD_ATTEMPT = readPwmConstantsBundle("defaultBadPasswordAttempt");
 
     public static final String CONTEXT_ATTR_CONTEXT_MANAGER = "ContextManager";
-    public static final String CONTEXT_ATTR_RESOURCE_DATA = "ResourceFileServlet-Data";
 
     public static final String SESSION_ATTR_PWM_SESSION = "PwmSession";
     public static final String SESSION_ATTR_BEANS = "SessionBeans";
     public static final String SESSION_ATTR_PWM_APP_NONCE = "PwmApplication-Nonce";
     public static final String SESSION_ATTR_FORGOTTEN_PW_USERINFO_CACHE = "ForgottenPw-UserInfoCache";
 
-    public static final PwmBlockAlgorithm IN_MEMORY_PASSWORD_ENCRYPT_METHOD = PwmBlockAlgorithm.AES;
     public static final PwmHashAlgorithm SETTING_CHECKSUM_HASH_METHOD = PwmHashAlgorithm.SHA256;
 
 
@@ -169,7 +164,7 @@ public abstract class PwmConstants {
     public static final String RESOURCE_FILE_WELCOME_TXT = "welcome.txt";
 
     // don't worry.  look over there.
-    public static final String[] X_AMB_HEADER = new String[]{
+    public static final List<String> X_AMB_HEADER = Collections.unmodifiableList(Arrays.asList(
             "bonjour!",
             "something witty!",
             "just like X-Fry, only ambier",
@@ -213,8 +208,8 @@ public abstract class PwmConstants {
             "passwords are like underwear, changing underwear regularly is a good thing.", //menno
             "daisy, daisy, give me your password do...",
             "it's a wholesome can of software goodness", // thx krowten
-            "this password is an memorial of the richard d. kiel memorial abend",
-    };
+            "this password is an memorial of the richard d. kiel memorial abend"
+    ));
 
 
     private static String readPwmConstantsBundle(final String key) {
@@ -234,9 +229,6 @@ public abstract class PwmConstants {
         return defaultValue;
     }
 
-// -------------------------- ENUMERATIONS --------------------------
-
-
     public enum AcceptValue {
         json("application/json"),
         html("text/html"),

+ 3 - 5
server/src/main/java/password/pwm/PwmEnvironment.java

@@ -340,11 +340,9 @@ public class PwmEnvironment {
                 return Collections.emptyMap();
             }
 
-            Properties propValues = null;
-            try {
-                final Properties newProps = new Properties();
-                newProps.load(new FileInputStream(new File(input)));
-                propValues = newProps;
+            final Properties propValues = new Properties();
+            try (FileInputStream fileInputStream = new FileInputStream(new File(input))) {
+                propValues.load(fileInputStream);
             } catch (Exception e) {
                 LOGGER.warn("error reading properties file '" + input + "' specified by environment setting " + EnvironmentParameter.applicationParamFile.toString() + ", error: " + e.getMessage());
             }

+ 16 - 6
server/src/main/java/password/pwm/bean/PrivateKeyCertificate.java

@@ -22,16 +22,26 @@
 
 package password.pwm.bean;
 
-import lombok.AllArgsConstructor;
-import lombok.Getter;
-
 import java.io.Serializable;
 import java.security.PrivateKey;
 import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.List;
 
-@Getter
-@AllArgsConstructor
 public class PrivateKeyCertificate implements Serializable {
-    private final X509Certificate[] certificates;
+    private final List<X509Certificate> certificates;
     private final PrivateKey key;
+
+    public PrivateKeyCertificate(final List<X509Certificate> certificates, final PrivateKey key) {
+        this.certificates = Collections.unmodifiableList(certificates);
+        this.key = key;
+    }
+
+    public List<X509Certificate> getCertificates() {
+        return Collections.unmodifiableList(certificates);
+    }
+
+    public PrivateKey getKey() {
+        return key;
+    }
 }

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

@@ -44,10 +44,10 @@ import password.pwm.config.profile.PwmPasswordPolicy;
 import password.pwm.config.profile.PwmPasswordRule;
 import password.pwm.config.profile.UpdateAttributesProfile;
 import password.pwm.config.stored.ConfigurationProperty;
-import password.pwm.config.value.CustomLinkValue;
 import password.pwm.config.stored.StoredConfigurationImpl;
 import password.pwm.config.stored.StoredConfigurationUtil;
 import password.pwm.config.value.BooleanValue;
+import password.pwm.config.value.CustomLinkValue;
 import password.pwm.config.value.FileValue;
 import password.pwm.config.value.FormValue;
 import password.pwm.config.value.LocalizedStringArrayValue;
@@ -80,6 +80,7 @@ import java.io.Serializable;
 import java.lang.reflect.InvocationTargetException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumMap;
@@ -95,19 +96,14 @@ import java.util.TreeMap;
 /**
  * @author Jason D. Rivard
  */
-public class Configuration implements Serializable, SettingReader {
-// ------------------------------ FIELDS ------------------------------
-
+public class Configuration implements SettingReader {
     private static final PwmLogger LOGGER = PwmLogger.forClass(Configuration.class);
 
     private final StoredConfigurationImpl storedConfiguration;
 
     private DataCache dataCache = new DataCache();
 
-    private String cachshedConfigurationHash;
-
-
-    // --------------------------- CONSTRUCTORS ---------------------------
+    private String cashedConfigurationHash;
 
     public Configuration(final StoredConfigurationImpl storedConfiguration) {
         this.storedConfiguration = storedConfiguration;
@@ -155,8 +151,9 @@ public class Configuration implements Serializable, SettingReader {
 
         final Map<String, EmailItemBean> storedValues = (Map<String, EmailItemBean>)readStoredValue(setting).toNativeObject();
         final Map<Locale, EmailItemBean> availableLocaleMap = new LinkedHashMap<>();
-        for (final String localeStr : storedValues.keySet()) {
-            availableLocaleMap.put(LocaleHelper.parseLocaleString(localeStr), storedValues.get(localeStr));
+        for (final Map.Entry<String, EmailItemBean> entry : storedValues.entrySet()) {
+            final String localeStr = entry.getKey();
+            availableLocaleMap.put(LocaleHelper.parseLocaleString(localeStr), entry.getValue());
         }
         final Locale matchedLocale = LocaleHelper.localeResolver(locale, availableLocaleMap.keySet());
 
@@ -345,8 +342,9 @@ public class Configuration implements Serializable, SettingReader {
 
             final Map<String, String> availableValues = (Map<String, String>)value.toNativeObject();
             final Map<Locale, String> availableLocaleMap = new LinkedHashMap<>();
-            for (final String localeStr : availableValues.keySet()) {
-                availableLocaleMap.put(LocaleHelper.parseLocaleString(localeStr), availableValues.get(localeStr));
+            for (final Map.Entry<String,String> entry : availableValues.entrySet()) {
+                final String localeStr = entry.getKey();
+                availableLocaleMap.put(LocaleHelper.parseLocaleString(localeStr), entry.getValue());
             }
             final Locale matchedLocale = LocaleHelper.localeResolver(locale, availableLocaleMap.keySet());
 
@@ -359,8 +357,9 @@ public class Configuration implements Serializable, SettingReader {
             }
             final Map<String, List<String>> storedValues = (Map<String, List<String>>)value.toNativeObject();
             final Map<Locale, List<String>> availableLocaleMap = new LinkedHashMap<>();
-            for (final String localeStr : storedValues.keySet()) {
-                availableLocaleMap.put(LocaleHelper.parseLocaleString(localeStr), storedValues.get(localeStr));
+            for (final Map.Entry<String, List<String>> entry : storedValues.entrySet()) {
+                final String localeStr = entry.getKey();
+                availableLocaleMap.put(LocaleHelper.parseLocaleString(localeStr), entry.getValue());
             }
             final Locale matchedLocale = LocaleHelper.localeResolver(locale, availableLocaleMap.keySet());
 
@@ -423,8 +422,9 @@ public class Configuration implements Serializable, SettingReader {
         }
 
         final Map<Locale,String> localizedMap = new LinkedHashMap<>();
-        for (final String localeKey : storedValue.keySet()) {
-            localizedMap.put(LocaleHelper.parseLocaleString(localeKey),storedValue.get(localeKey));
+        for (final Map.Entry<String,String> entry: storedValue.entrySet()) {
+            final String localeKey = entry.getKey();
+            localizedMap.put(LocaleHelper.parseLocaleString(localeKey),entry.getValue());
         }
 
         dataCache.customText.put(key, localizedMap);
@@ -580,14 +580,15 @@ public class Configuration implements Serializable, SettingReader {
         return (Map)fileValue.toNativeObject();
     }
 
-    public X509Certificate[] readSettingAsCertificate(final PwmSetting setting) {
+    public List<X509Certificate> readSettingAsCertificate(final PwmSetting setting) {
         if (PwmSettingSyntax.X509CERT != setting.getSyntax()) {
             throw new IllegalArgumentException("may not read X509CERT value for setting: " + setting.toString());
         }
         if (readStoredValue(setting) == null) {
-            return new X509Certificate[0];
+            return Collections.emptyList();
         }
-        return (X509Certificate[])readStoredValue(setting).toNativeObject();
+        final X509Certificate[] arrayCerts = (X509Certificate[])readStoredValue(setting).toNativeObject();
+        return arrayCerts == null ? Collections.emptyList() : Arrays.asList(arrayCerts);
     }
 
     public PrivateKeyCertificate readSettingAsPrivateKey(final PwmSetting setting) {
@@ -699,8 +700,7 @@ public class Configuration implements Serializable, SettingReader {
 
         //ensure default is first.
         returnList.add(defaultLocaleAsString);
-        for (final String localeDisplayString : sortedMap.keySet()) {
-            final String localeString = sortedMap.get(localeDisplayString);
+        for (final String localeString : sortedMap.values()) {
             if (!defaultLocaleAsString.equals(localeString)) {
                 returnList.add(localeString);
             }
@@ -853,8 +853,8 @@ public class Configuration implements Serializable, SettingReader {
     public Map<String,NewUserProfile> getNewUserProfiles() {
         final Map<String,NewUserProfile> returnMap = new LinkedHashMap<>();
         final Map<String,Profile> profileMap = profileMap(ProfileType.NewUser);
-        for (final String profileID : profileMap.keySet()) {
-            returnMap.put(profileID, (NewUserProfile)profileMap.get(profileID));
+        for (final Map.Entry<String,Profile> entry : profileMap.entrySet()) {
+            returnMap.put(entry.getKey(), (NewUserProfile) entry.getValue());
         }
         return returnMap;
     }
@@ -862,8 +862,8 @@ public class Configuration implements Serializable, SettingReader {
     public Map<String,HelpdeskProfile> getHelpdeskProfiles() {
         final Map<String,HelpdeskProfile> returnMap = new LinkedHashMap<>();
         final Map<String,Profile> profileMap = profileMap(ProfileType.Helpdesk);
-        for (final String profileID : profileMap.keySet()) {
-            returnMap.put(profileID, (HelpdeskProfile)profileMap.get(profileID));
+        for (final Map.Entry<String,Profile> entry: profileMap.entrySet()) {
+            returnMap.put(entry.getKey(), (HelpdeskProfile)entry.getValue());
         }
         return returnMap;
     }
@@ -871,8 +871,8 @@ public class Configuration implements Serializable, SettingReader {
     public Map<String,UpdateAttributesProfile> getUpdateAttributesProfile() {
         final Map<String,UpdateAttributesProfile> returnMap = new LinkedHashMap<>();
         final Map<String,Profile> profileMap = profileMap(ProfileType.UpdateAttributes);
-        for (final String profileID : profileMap.keySet()) {
-            returnMap.put(profileID, (UpdateAttributesProfile)profileMap.get(profileID));
+        for (final Map.Entry<String,Profile> entry : profileMap.entrySet()) {
+            returnMap.put(entry.getKey(), (UpdateAttributesProfile) entry.getValue());
         }
         return returnMap;
     }
@@ -880,8 +880,8 @@ public class Configuration implements Serializable, SettingReader {
     public Map<String,ForgottenPasswordProfile> getForgottenPasswordProfiles() {
         final Map<String,ForgottenPasswordProfile> returnMap = new LinkedHashMap<>();
         final Map<String,Profile> profileMap = profileMap(ProfileType.ForgottenPassword);
-        for (final String profileID : profileMap.keySet()) {
-            returnMap.put(profileID, (ForgottenPasswordProfile)profileMap.get(profileID));
+        for (final Map.Entry<String,Profile> entry : profileMap.entrySet()) {
+            returnMap.put(entry.getKey(), (ForgottenPasswordProfile) entry.getValue());
         }
         return returnMap;
     }
@@ -939,10 +939,10 @@ public class Configuration implements Serializable, SettingReader {
     public String configurationHash()
             throws PwmUnrecoverableException 
     {
-        if (this.cachshedConfigurationHash == null) {
-            this.cachshedConfigurationHash = storedConfiguration.settingChecksum();
+        if (this.cashedConfigurationHash == null) {
+            this.cashedConfigurationHash = storedConfiguration.settingChecksum();
         }
-        return cachshedConfigurationHash;
+        return cashedConfigurationHash;
     }
 
     public Set<PwmSetting> nonDefaultSettings() {

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

@@ -38,6 +38,7 @@ import password.pwm.util.macro.MacroMachine;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.List;
@@ -1162,18 +1163,79 @@ public enum PwmSetting {
     private final PwmSettingSyntax syntax;
     private final PwmSettingCategory category;
 
-    private List<TemplateSetAssociation> defaultValues;
-    private List<TemplateSetAssociation> examples;
-    private Map<String,String> options;
+    private static final Map<PwmSetting,List<TemplateSetAssociation>> DEFAULT_VALUES;
+    private static final Map<PwmSetting, Map<String,String>> OPTIONS;
+    private static final Map<PwmSetting, List<TemplateSetAssociation>> EXAMPLES;
     private Collection<PwmSettingFlag> flags;
     private Boolean required;
     private Boolean hidden;
     private Integer level;
     private Pattern pattern;
 
+    static {
+        final Map<PwmSetting,List<TemplateSetAssociation>> returnMap = new HashMap<>();
+        for (final PwmSetting pwmSetting : PwmSetting.values()) {
+            final List<TemplateSetAssociation> returnObj = new ArrayList<>();
+            final Element settingElement = PwmSettingXml.readSettingXml(pwmSetting);
+            final List<Element> defaultElements = settingElement.getChildren(PwmSettingXml.XML_ELEMENT_DEFAULT);
+            if (pwmSetting.getSyntax() == PwmSettingSyntax.PASSWORD) {
+                returnObj.add(new TemplateSetAssociation(new PasswordValue(null), Collections.emptySet()));
+            } else {
+                for (final Element defaultElement : defaultElements) {
+                    final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute(defaultElement);
+                    final StoredValue storedValue = ValueFactory.fromXmlValues(pwmSetting, defaultElement, null);
+                    returnObj.add(new TemplateSetAssociation(storedValue, definedTemplates));
+                }
+            }
+            if (returnObj.isEmpty()) {
+                throw new IllegalStateException("no default value for setting " + pwmSetting.getKey());
+            }
+            returnMap.put(pwmSetting, returnObj);
+        }
+        DEFAULT_VALUES = Collections.unmodifiableMap(returnMap);
+    }
 
+    static {
+        final Map<PwmSetting, Map<String,String>> returnObj = new HashMap<>();
+        for (final PwmSetting pwmSetting : PwmSetting.values()) {
+            final Map<String, String> returnList = new LinkedHashMap<>();
+            final Element settingElement = PwmSettingXml.readSettingXml(pwmSetting);
+            final Element optionsElement = settingElement.getChild("options");
+            if (optionsElement != null) {
+                final List<Element> optionElements = optionsElement.getChildren("option");
+                if (optionElements != null) {
+                    for (final Element optionElement : optionElements) {
+                        if (optionElement.getAttribute("value") == null) {
+                            throw new IllegalStateException("option element is missing 'value' attribute for key " + pwmSetting.getKey());
+                        }
+                        returnList.put(optionElement.getAttribute("value").getValue(), optionElement.getValue());
+                    }
+                }
+            }
+            returnObj.put(pwmSetting, Collections.unmodifiableMap(returnList));
+        }
+        OPTIONS = Collections.unmodifiableMap(returnObj);
+    }
 
-// --------------------------- CONSTRUCTORS ---------------------------
+    static {
+        final Map<PwmSetting, List<TemplateSetAssociation>> returnMap = new HashMap<>();
+        for (final PwmSetting pwmSetting : PwmSetting.values()) {
+            final List<TemplateSetAssociation> returnObj = new ArrayList<>();
+            final MacroMachine macroMachine = MacroMachine.forStatic();
+            final Element settingElement = PwmSettingXml.readSettingXml(pwmSetting);
+            final List<Element> exampleElements = settingElement.getChildren(PwmSettingXml.XML_ELEMENT_EXAMPLE);
+            for (final Element exampleElement : exampleElements) {
+                final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute(exampleElement);
+                final String exampleString = macroMachine.expandMacros(exampleElement.getText());
+                returnObj.add(new TemplateSetAssociation(exampleString, Collections.unmodifiableSet(definedTemplates)));
+            }
+            if (returnObj.isEmpty()) {
+                returnObj.add(new TemplateSetAssociation("", Collections.emptySet()));
+            }
+            returnMap.put(pwmSetting, Collections.unmodifiableList(returnObj));
+        }
+        EXAMPLES = Collections.unmodifiableMap(returnMap);
+    }
 
     PwmSetting(
             final String key,
@@ -1185,10 +1247,6 @@ public enum PwmSetting {
         this.category = category;
     }
 
-
-// --------------------- GETTER / SETTER METHODS ---------------------
-
-
     public String getKey() {
         return key;
     }
@@ -1205,40 +1263,18 @@ public enum PwmSetting {
         return syntax;
     }
 
-
-    // -------------------------- OTHER METHODS --------------------------
-
     public StoredValue getDefaultValue(final PwmSettingTemplateSet templateSet)
             throws PwmOperationalException, PwmUnrecoverableException
     {
-        if (defaultValues == null) {
-            final List<TemplateSetAssociation> returnObj = new ArrayList<>();
-            final Element settingElement = PwmSettingXml.readSettingXml(this);
-            final List<Element> defaultElements = settingElement.getChildren(PwmSettingXml.XML_ELEMENT_DEFAULT);
-            if (this.getSyntax() == PwmSettingSyntax.PASSWORD) {
-                returnObj.add(new TemplateSetAssociation(new PasswordValue(null), Collections.<PwmSettingTemplate>emptySet()));
-            } else {
-                for (final Element defaultElement : defaultElements) {
-                    final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute(defaultElement);
-                    final StoredValue storedValue = ValueFactory.fromXmlValues(this, defaultElement, null);
-                    returnObj.add(new TemplateSetAssociation(storedValue, definedTemplates));
-                }
-            }
-            if (returnObj.isEmpty()) {
-                throw new IllegalStateException("no default value for setting " + this.getKey());
-            }
-            defaultValues = Collections.unmodifiableList(returnObj);
-        }
-
+        final List<TemplateSetAssociation> defaultValues = DEFAULT_VALUES.get(this);
         return (StoredValue)associationForTempleSet(defaultValues, templateSet).getObject();
     }
 
     public Map<String,String> getDefaultValueDebugStrings(final Locale locale)
             throws PwmOperationalException, PwmUnrecoverableException
     {
-        getDefaultValue(PwmSettingTemplateSet.getDefault()); // ensure value has been read into cache
         final Map<String,String> returnObj = new LinkedHashMap<>();
-        for (final TemplateSetAssociation templateSetAssociation : defaultValues) {
+        for (final TemplateSetAssociation templateSetAssociation : DEFAULT_VALUES.get(this)) {
             returnObj.put(
                     StringUtil.join(templateSetAssociation.getSettingTemplates(),","),
                     ((StoredValue) templateSetAssociation.getObject()).toDebugString(locale)
@@ -1248,25 +1284,7 @@ public enum PwmSetting {
     }
 
     public Map<String, String> getOptions() {
-        if (options == null) {
-            final Map<String, String> returnList = new LinkedHashMap<>();
-            final Element settingElement = PwmSettingXml.readSettingXml(this);
-            final Element optionsElement = settingElement.getChild("options");
-            if (optionsElement != null) {
-                final List<Element> optionElements = optionsElement.getChildren("option");
-                if (optionElements != null) {
-                    for (final Element optionElement : optionElements) {
-                        if (optionElement.getAttribute("value") == null) {
-                            throw new IllegalStateException("option element is missing 'value' attribute for key " + this.getKey());
-                        }
-                        returnList.put(optionElement.getAttribute("value").getValue(), optionElement.getValue());
-                    }
-                }
-            }
-            options = Collections.unmodifiableMap(returnList);
-        }
-
-        return options;
+        return OPTIONS.get(this);
     }
 
     public Map<PwmSettingProperty, String> getProperties() {
@@ -1353,21 +1371,7 @@ public enum PwmSetting {
     }
 
     public String getExample(final PwmSettingTemplateSet template) {
-        if (examples == null) {
-            final List<TemplateSetAssociation> returnObj = new ArrayList<>();
-            final MacroMachine macroMachine = MacroMachine.forStatic();
-            final Element settingElement = PwmSettingXml.readSettingXml(this);
-            final List<Element> exampleElements = settingElement.getChildren(PwmSettingXml.XML_ELEMENT_EXAMPLE);
-            for (final Element exampleElement : exampleElements) {
-                final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute(exampleElement);
-                final String exampleString = macroMachine.expandMacros(exampleElement.getText());
-                returnObj.add(new TemplateSetAssociation(exampleString, definedTemplates));
-            }
-            if (returnObj.isEmpty()) {
-                returnObj.add(new TemplateSetAssociation("",Collections.<PwmSettingTemplate>emptySet()));
-            }
-            examples = returnObj;
-        }
+        final List<TemplateSetAssociation> examples = EXAMPLES.get(this);
         return (String)associationForTempleSet(examples,template).getObject();
     }
 

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

@@ -40,7 +40,7 @@ public interface SettingReader {
 
     <E extends Enum<E>> E readSettingAsEnum(PwmSetting setting, Class<E> enumClass);
 
-    X509Certificate[] readSettingAsCertificate(PwmSetting setting);
+    List<X509Certificate> readSettingAsCertificate(PwmSetting setting);
 
     boolean readSettingAsBoolean(PwmSetting setting);
 

+ 12 - 2
server/src/main/java/password/pwm/config/function/AbstractUriCertImportFunction.java

@@ -38,6 +38,7 @@ import password.pwm.util.secure.X509Utils;
 
 import java.net.URI;
 import java.security.cert.X509Certificate;
+import java.util.List;
 
 abstract class AbstractUriCertImportFunction implements SettingUIFunction {
 
@@ -51,7 +52,7 @@ abstract class AbstractUriCertImportFunction implements SettingUIFunction {
             throws PwmOperationalException, PwmUnrecoverableException
     {
         final PwmSession pwmSession = pwmRequest.getPwmSession();
-        final X509Certificate[] certs;
+        final List<X509Certificate> certs;
 
         final String urlString = getUri(storedConfiguration, setting, profile, extraData);
             try {
@@ -79,7 +80,16 @@ abstract class AbstractUriCertImportFunction implements SettingUIFunction {
     abstract String getUri(StoredConfigurationImpl storedConfiguration,  PwmSetting pwmSetting,  String profile,  String extraData) throws PwmOperationalException;
 
 
-    void store(final X509Certificate[] certs, final StoredConfigurationImpl storedConfiguration, final PwmSetting pwmSetting, final String profile, final String extraData, final UserIdentity userIdentity) throws PwmOperationalException, PwmUnrecoverableException {
+    void store(
+            final List<X509Certificate> certs,
+            final StoredConfigurationImpl storedConfiguration,
+            final PwmSetting pwmSetting,
+            final String profile,
+            final String extraData,
+            final UserIdentity userIdentity
+    )
+            throws PwmOperationalException, PwmUnrecoverableException
+    {
         storedConfiguration.writeSetting(pwmSetting, profile, new X509CertificateValue(certs), userIdentity);
     }
 

+ 10 - 1
server/src/main/java/password/pwm/config/function/ActionCertImportFunction.java

@@ -64,7 +64,16 @@ public class ActionCertImportFunction extends AbstractUriCertImportFunction {
         return extraData;
     }
 
-    void store(final X509Certificate[] certs, final StoredConfigurationImpl storedConfiguration, final PwmSetting pwmSetting, final String profile, final String extraData, final UserIdentity userIdentity) throws PwmOperationalException, PwmUnrecoverableException {
+    void store(
+            final List<X509Certificate> certs,
+            final StoredConfigurationImpl storedConfiguration,
+            final PwmSetting pwmSetting,
+            final String profile,
+            final String extraData,
+            final UserIdentity userIdentity
+    )
+            throws PwmOperationalException, PwmUnrecoverableException
+    {
         final ActionValue actionValue = (ActionValue)storedConfiguration.readSetting(pwmSetting, profile);
         final String actionName = actionNameFromExtraData(extraData);
         final List<ActionConfiguration> newList = new ArrayList<>();

+ 2 - 3
server/src/main/java/password/pwm/config/function/LdapCertImportFunction.java

@@ -41,7 +41,6 @@ import password.pwm.util.secure.X509Utils;
 
 import java.net.URI;
 import java.security.cert.X509Certificate;
-import java.util.Arrays;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
@@ -66,9 +65,9 @@ public class LdapCertImportFunction implements SettingUIFunction {
                 final List<String> ldapUrlStrings = ldapUrlsValue.toNativeObject();
                 for (final String ldapUrlString : ldapUrlStrings) {
                     final URI ldapURI = new URI(ldapUrlString);
-                    final X509Certificate[] certs = X509Utils.readRemoteCertificates(ldapURI);
+                    final List<X509Certificate> certs = X509Utils.readRemoteCertificates(ldapURI);
                     if (certs != null) {
-                        resultCertificates.addAll(Arrays.asList(certs));
+                        resultCertificates.addAll(certs);
                     }
                 }
             }

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

@@ -64,7 +64,7 @@ public class RemoteWebServiceCertImportFunction extends AbstractUriCertImportFun
         return extraData;
     }
 
-    void store(final X509Certificate[] certs, final StoredConfigurationImpl storedConfiguration, final PwmSetting pwmSetting, final String profile, final String extraData, final UserIdentity userIdentity) throws PwmOperationalException, PwmUnrecoverableException {
+    void store(final List<X509Certificate> certs, final StoredConfigurationImpl storedConfiguration, final PwmSetting pwmSetting, final String profile, final String extraData, final UserIdentity userIdentity) throws PwmOperationalException, PwmUnrecoverableException {
         final RemoteWebServiceValue actionValue = (RemoteWebServiceValue)storedConfiguration.readSetting(pwmSetting, profile);
         final String actionName = actionNameFromExtraData(extraData);
         final List<RemoteWebServiceConfiguration> newList = new ArrayList<>();

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

@@ -40,8 +40,8 @@ import password.pwm.svc.event.SyslogAuditService;
 import password.pwm.util.secure.X509Utils;
 
 import java.security.cert.X509Certificate;
-import java.util.Arrays;
 import java.util.LinkedHashSet;
+import java.util.List;
 import java.util.Set;
 
 public class SyslogCertImportFunction implements SettingUIFunction {
@@ -64,9 +64,9 @@ public class SyslogCertImportFunction implements SettingUIFunction {
             final SyslogAuditService.SyslogConfig syslogConfig = SyslogAuditService.SyslogConfig.fromConfigString(syslogConfigStr);
             if (syslogConfig != null) {
                 try {
-                    final X509Certificate[] certs = X509Utils.readRemoteCertificates(syslogConfig.getHost(), syslogConfig.getPort());
+                    final List<X509Certificate> certs = X509Utils.readRemoteCertificates(syslogConfig.getHost(), syslogConfig.getPort());
                     if (certs != null) {
-                        resultCertificates.addAll(Arrays.asList(certs));
+                        resultCertificates.addAll(certs);
                     }
                 } catch (Exception e) {
                     if (e instanceof PwmException) {

+ 5 - 4
server/src/main/java/password/pwm/config/option/IdentityVerificationMethod.java

@@ -25,12 +25,13 @@ package password.pwm.config.option;
 import password.pwm.config.Configuration;
 import password.pwm.i18n.Display;
 
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
 
-public enum IdentityVerificationMethod implements ConfigurationOption {
+public enum IdentityVerificationMethod implements Serializable, ConfigurationOption {
     PREVIOUS_AUTH(      false,  Display.Field_VerificationMethodPreviousAuth,       Display.Description_VerificationMethodPreviousAuth),
     ATTRIBUTES(         true,   Display.Field_VerificationMethodAttributes,         Display.Description_VerificationMethodAttributes),
     CHALLENGE_RESPONSES(true,   Display.Field_VerificationMethodChallengeResponses, Display.Description_VerificationMethodChallengeResponses),
@@ -41,9 +42,9 @@ public enum IdentityVerificationMethod implements ConfigurationOption {
 
     ;
     
-    private final boolean userSelectable;
-    private final Display labelKey;
-    private final Display descriptionKey;
+    private final transient boolean userSelectable;
+    private final transient Display labelKey;
+    private final transient Display descriptionKey;
 
     IdentityVerificationMethod(final boolean userSelectable, final Display labelKey, final Display descriptionKey) {
         this.userSelectable = userSelectable;

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

@@ -38,6 +38,8 @@ import password.pwm.util.PasswordData;
 
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
@@ -94,14 +96,15 @@ public abstract class AbstractProfile implements Profile, SettingReader {
     }
 
     @Override
-    public X509Certificate[] readSettingAsCertificate(final PwmSetting setting) {
+    public List<X509Certificate> readSettingAsCertificate(final PwmSetting setting) {
         if (PwmSettingSyntax.X509CERT != setting.getSyntax()) {
             throw new IllegalArgumentException("may not read X509CERT value for setting: " + setting.toString());
         }
         if (storedValueMap.containsKey(setting)) {
-            return (X509Certificate[])storedValueMap.get(setting).toNativeObject();
+            final X509Certificate[] arrayCert = (X509Certificate[])storedValueMap.get(setting).toNativeObject();
+            return arrayCert == null ? Collections.emptyList() : Arrays.asList(arrayCert);
         }
-        return new X509Certificate[0];
+        return Collections.emptyList();
     }
 
     @Override

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

@@ -242,8 +242,9 @@ public class ChallengeProfile implements Profile, Serializable {
         }
         final Map<String, List<ChallengeItemConfiguration>> storedValues = (Map<String, List<ChallengeItemConfiguration>>)value.toNativeObject();
         final Map<Locale, List<ChallengeItemConfiguration>> availableLocaleMap = new LinkedHashMap<>();
-        for (final String localeStr : storedValues.keySet()) {
-            availableLocaleMap.put(LocaleHelper.parseLocaleString(localeStr), storedValues.get(localeStr));
+        for (final Map.Entry<String,List<ChallengeItemConfiguration>> entry : storedValues.entrySet()) {
+            final String localeStr = entry.getKey();
+            availableLocaleMap.put(LocaleHelper.parseLocaleString(localeStr), entry.getValue());
         }
         final Locale matchedLocale = LocaleHelper.localeResolver(locale, availableLocaleMap.keySet());
 

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

@@ -70,8 +70,9 @@ public class LdapProfile extends AbstractProfile implements Profile {
         final List<String> rawValues = readSettingAsStringArray(PwmSetting.LDAP_LOGIN_CONTEXTS);
         final Map<String, String> configuredValues = StringUtil.convertStringListToNameValuePair(rawValues, ":::");
         final Map<String, String> canonicalValues = new LinkedHashMap<>();
-        for (final String dn : configuredValues.keySet() ) {
-            final String label = configuredValues.get(dn);
+        for (final Map.Entry<String,String> entry : configuredValues.entrySet()) {
+            final String dn = entry.getKey();
+            final String label = entry.getValue();
             final String canonicalDN = readCanonicalDN(pwmApplication, dn);
             canonicalValues.put(canonicalDN, label);
         }

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

@@ -48,8 +48,7 @@ public class ProfileUtility {
             throws PwmUnrecoverableException
     {
         final Map<String,Profile> profileMap = pwmApplication.getConfig().profileMap(profileType);
-        for (final String profileID : profileMap.keySet()) {
-            final Profile profile = profileMap.get(profileID);
+        for (final Profile profile : profileMap.values()) {
             final List<UserPermission> queryMatches = profile.getPermissionMatches();
             final boolean match = LdapPermissionTester.testUserPermissions(pwmApplication, sessionLabel, userIdentity, queryMatches);
             if (match) {
@@ -61,8 +60,7 @@ public class ProfileUtility {
 
     public static List<String> profileIDsForCategory(final Configuration configuration, final PwmSettingCategory pwmSettingCategory) {
         final PwmSetting profileSetting = pwmSettingCategory.getProfileSetting();
-        final List<String> profileIDs = configuration.readSettingAsStringArray(profileSetting);
-        return profileIDs;
+        return configuration.readSettingAsStringArray(profileSetting);
     }
 
 

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

@@ -55,7 +55,6 @@ import java.util.regex.PatternSyntaxException;
  * @author Jason D. Rivard
  */
 public class PwmPasswordPolicy implements Profile,Serializable {
-// ------------------------------ FIELDS ------------------------------
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(PwmPasswordPolicy.class);
 
@@ -84,8 +83,6 @@ public class PwmPasswordPolicy implements Profile,Serializable {
         return getIdentifier();
     }
 
-// -------------------------- STATIC METHODS --------------------------
-
     static {
         PwmPasswordPolicy newDefaultPolicy = null;
         try {
@@ -273,9 +270,6 @@ public class PwmPasswordPolicy implements Profile,Serializable {
         return createPwmPasswordPolicy(policyMap, null);
     }
 
-
-// -------------------------- INNER CLASSES --------------------------
-
     public static class RuleHelper {
         public enum Flag { KeepThresholds }
 
@@ -429,8 +423,9 @@ public class PwmPasswordPolicy implements Profile,Serializable {
         rulePairs.put(PwmPasswordRule.MinimumNonAlpha, PwmPasswordRule.MaximumNonAlpha);
         rulePairs.put(PwmPasswordRule.MinimumUnique, PwmPasswordRule.MaximumUnique);
 
-        for (final PwmPasswordRule minRule : rulePairs.keySet()) {
-            final PwmPasswordRule maxRule = rulePairs.get(minRule);
+        for (final Map.Entry<PwmPasswordRule, PwmPasswordRule> entry : rulePairs.entrySet()) {
+            final PwmPasswordRule minRule = entry.getKey();
+            final PwmPasswordRule maxRule = entry.getValue();
 
             final int minValue = ruleHelper.readIntValue(minRule);
             final int maxValue = ruleHelper.readIntValue(maxRule);

+ 5 - 6
server/src/main/java/password/pwm/config/stored/ConfigChangeLogImpl.java

@@ -24,8 +24,6 @@ package password.pwm.config.stored;
 
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
-import password.pwm.i18n.Config;
-import password.pwm.util.LocaleHelper;
 import password.pwm.util.java.StringUtil;
 
 import java.io.Serializable;
@@ -38,7 +36,7 @@ import java.util.TreeMap;
 public class ConfigChangeLogImpl implements Serializable, ConfigChangeLog {
     private final Map<StoredConfigReference,StoredValue> changeLog = new LinkedHashMap<>();
     private final Map<StoredConfigReference,StoredValue> originalValue = new LinkedHashMap<>();
-    private final StorageEngine storedConfiguration;
+    private final transient StorageEngine storedConfiguration;
 
     public ConfigChangeLogImpl(final StorageEngine storageEngine) {
         this.storedConfiguration = storageEngine;
@@ -52,7 +50,6 @@ public class ConfigChangeLogImpl implements Serializable, ConfigChangeLog {
     @Override
     public String changeLogAsDebugString(final Locale locale, final boolean asHtml) {
         final Map<String,String> outputMap = new TreeMap<>();
-        final String SEPARATOR = LocaleHelper.getLocalizedMessage(locale, Config.Display_SettingNavigationSeparator, null);
 
         for (final StoredConfigReference configReference : changeLog.keySet()) {
             switch (configReference.getRecordType()) {
@@ -67,6 +64,7 @@ public class ConfigChangeLogImpl implements Serializable, ConfigChangeLog {
 
                 /*
                 case LOCALE_BUNDLE: {
+                    final String SEPARATOR = LocaleHelper.getLocalizedMessage(locale, Config.Display_SettingNavigationSeparator, null);
                     final String key = (String) configReference.recordID;
                     final String bundleName = key.split("!")[0];
                     final String keys = key.split("!")[1];
@@ -86,8 +84,9 @@ public class ConfigChangeLogImpl implements Serializable, ConfigChangeLog {
         if (outputMap.isEmpty()) {
             output.append("No setting changes.");
         } else {
-            for (final String keyName : outputMap.keySet()) {
-                final String value = outputMap.get(keyName);
+            for (final Map.Entry<String,String> entry  : outputMap.entrySet()) {
+                final String keyName = entry.getKey();
+                final String value = entry.getValue();
                 if (asHtml) {
                     output.append("<div class=\"changeLogKey\">");
                     output.append(keyName);

+ 15 - 11
server/src/main/java/password/pwm/config/stored/ConfigurationReader.java

@@ -56,8 +56,6 @@ import java.util.List;
  * @author Jason D. Rivard
  */
 public class ConfigurationReader {
-// ------------------------------ FIELDS ------------------------------
-
     private static final PwmLogger LOGGER = PwmLogger.getLogger(ConfigurationReader.class.getName());
 
     private final File configFile;
@@ -101,8 +99,13 @@ public class ConfigurationReader {
 
     public Configuration getConfiguration() throws PwmUnrecoverableException {
         if (configuration == null) {
-            configuration = new Configuration(this.storedConfiguration == null ? StoredConfigurationImpl.newStoredConfiguration() : this.storedConfiguration);
-            storedConfiguration.lock();
+            final StoredConfigurationImpl newStoredConfig = this.storedConfiguration == null
+                    ? StoredConfigurationImpl.newStoredConfiguration()
+                    : this.storedConfiguration;
+            configuration = new Configuration(newStoredConfig);
+            if (storedConfiguration != null) {
+                storedConfiguration.lock();
+            }
         }
         return configuration;
     }
@@ -164,7 +167,7 @@ public class ConfigurationReader {
             final StoredConfigurationImpl storedConfiguration,
             final PwmApplication pwmApplication,
             final SessionLabel sessionLabel
-            )
+    )
             throws IOException, PwmUnrecoverableException, PwmOperationalException
     {
         File backupDirectory = null;
@@ -203,7 +206,10 @@ public class ConfigurationReader {
             LOGGER.info(sessionLabel, "beginning write to configuration file " + tempWriteFile);
             saveInProgress = true;
 
-            storedConfiguration.toXml(new FileOutputStream(tempWriteFile, false));
+            try (FileOutputStream fileOutputStream = new FileOutputStream(tempWriteFile, false)) {
+                storedConfiguration.toXml(fileOutputStream);
+            }
+
             LOGGER.info("saved configuration " + JsonUtil.serialize(storedConfiguration.toJsonDebugObject()));
             if (pwmApplication != null) {
                 final String actualChecksum = storedConfiguration.settingChecksum();
@@ -224,7 +230,9 @@ public class ConfigurationReader {
                 final String backupFilePath = backupDirectory.getAbsolutePath() + File.separatorChar + configFileName + "-backup";
                 final File backupFile = new File(backupFilePath);
                 FileSystemUtility.rotateBackups(backupFile, backupRotations);
-                storedConfiguration.toXml(new FileOutputStream(backupFile, false));
+                try (FileOutputStream fileOutputStream = new FileOutputStream(backupFile, false)) {
+                    storedConfiguration.toXml(fileOutputStream);
+                }
             }
         } finally {
             saveInProgress = false;
@@ -244,10 +252,6 @@ public class ConfigurationReader {
         return String.valueOf(file.lastModified() + String.valueOf(file.length()));
     }
 
-    public Date getConfigurationReadTime() {
-        return configurationReadTime;
-    }
-
     public ErrorInformation getConfigFileError() {
         return configFileError;
     }

+ 4 - 10
server/src/main/java/password/pwm/config/stored/NGStoredConfigurationFactory.java

@@ -29,7 +29,6 @@ import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.value.StringValue;
 import password.pwm.config.value.ValueFactory;
-import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.XmlUtil;
@@ -138,7 +137,7 @@ public class NGStoredConfigurationFactory {
                 if (settingElement.getChild(StoredConfiguration.XML_ELEMENT_DEFAULT) != null) {
                     try {
                         return ValueFactory.fromXmlValues(pwmSetting, settingElement, pwmSecurityKey);
-                    } catch (PwmException e) {
+                    } catch (IllegalStateException e) {
                         LOGGER.error("error parsing configuration setting " + storedConfigReference + ", error: " + e.getMessage());
                     }
                 }
@@ -152,15 +151,10 @@ public class NGStoredConfigurationFactory {
         )
         {
             final String key = storedConfigReference.getRecordID();
-            final ConfigurationProperty configProperty = ConfigurationProperty.valueOf(key);
 
-            if (configProperty == null) {
-                LOGGER.debug("ignoring property for unknown key: " + key);
-            } else {
-                LOGGER.trace("parsing property key=" + key + ", profile=" + storedConfigReference.getProfileID());
-                if (settingElement.getChild(StoredConfiguration.XML_ELEMENT_DEFAULT) != null) {
-                    return new StringValue(settingElement.getValue());
-                }
+            LOGGER.trace("parsing property key=" + key + ", profile=" + storedConfigReference.getProfileID());
+            if (settingElement.getChild(StoredConfiguration.XML_ELEMENT_DEFAULT) != null) {
+                return new StringValue(settingElement.getValue());
             }
             return null;
         }

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

@@ -58,7 +58,9 @@ class StoredConfigReferenceBean implements StoredConfigReference, Serializable,
 
     @Override
     public boolean equals(final Object o) {
-        return o instanceof StoredConfigReference && toString().equals(o);
+        return o != null
+                && o instanceof StoredConfigReference
+                && toString().equals(o.toString());
 
     }
 

+ 17 - 17
server/src/main/java/password/pwm/config/stored/StoredConfigurationImpl.java

@@ -97,8 +97,8 @@ import java.util.stream.Collectors;
 /**
  * @author Jason D. Rivard
  */
-public class StoredConfigurationImpl implements Serializable, StoredConfiguration {
-// ------------------------------ FIELDS ------------------------------
+@SuppressWarnings("all") // this class will be replaced by NGStoredConfiguration
+public class StoredConfigurationImpl implements StoredConfiguration {
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(StoredConfigurationImpl.class);
     private static final String XML_FORMAT_VERSION = "4";
@@ -110,8 +110,6 @@ public class StoredConfigurationImpl implements Serializable, StoredConfiguratio
     private final boolean setting_writeLabels = true;
     private final ReentrantReadWriteLock domModifyLock = new ReentrantReadWriteLock();
 
-// -------------------------- STATIC METHODS --------------------------
-
     public static StoredConfigurationImpl newStoredConfiguration() throws PwmUnrecoverableException {
         return new StoredConfigurationImpl();
     }
@@ -125,7 +123,7 @@ public class StoredConfigurationImpl implements Serializable, StoredConfiguratio
     public static StoredConfigurationImpl fromXml(final InputStream xmlData)
             throws PwmUnrecoverableException
     {
-        final Date startTime = new Date();
+        final Instant startTime = Instant.now();
         //validateXmlSchema(xmlData);
 
         final Document inputDocument = XmlUtil.parseXml(xmlData);
@@ -142,8 +140,7 @@ public class StoredConfigurationImpl implements Serializable, StoredConfiguratio
         }
 
         checkIfXmlRequiresUpdate(newConfiguration);
-        final TimeDuration totalDuration = TimeDuration.fromCurrent(startTime);
-        LOGGER.debug("successfully loaded configuration (" + totalDuration.asCompactString() + ")");
+        LOGGER.debug("successfully loaded configuration (" + TimeDuration.compactFromCurrent(startTime) + ")");
         return newConfiguration;
     }
 
@@ -320,8 +317,8 @@ public class StoredConfigurationImpl implements Serializable, StoredConfiguratio
     public void resetSetting(final PwmSetting setting, final String profileID, final UserIdentity userIdentity) {
         changeLog.updateChangeLog(setting, profileID, defaultValue(setting, this.getTemplateSet()));
         domModifyLock.writeLock().lock();
-        preModifyActions();
         try {
+            preModifyActions();
             final Element settingElement = createOrGetSettingElement(document, setting, profileID);
             settingElement.removeContent();
             settingElement.addContent(new Element(XML_ELEMENT_DEFAULT));
@@ -377,7 +374,7 @@ public class StoredConfigurationImpl implements Serializable, StoredConfiguratio
             try {
                 final String strValue = (String) ValueFactory.fromXmlValues(pwmSetting, settingElement, null).toNativeObject();
                 return JavaHelper.readEnumFromString(PwmSettingTemplate.class, null, strValue);
-            } catch (PwmException e) {
+            } catch (IllegalStateException e) {
                 LOGGER.error("error reading template", e);
             }
         }
@@ -717,12 +714,14 @@ public class StoredConfigurationImpl implements Serializable, StoredConfiguratio
             final Element localeBundleElement = new Element("localeBundle");
             localeBundleElement.setAttribute("bundle",bundleName);
             localeBundleElement.setAttribute("key",keyName);
-            for (final String locale : localeMap.keySet()) {
+            for (final Map.Entry<String,String> entry : localeMap.entrySet()) {
+                final String locale = entry.getKey();
+                final String value = entry.getValue();
                 final Element valueElement = new Element("value");
                 if (locale != null && locale.length() > 0) {
                     valueElement.setAttribute("locale",locale);
                 }
-                valueElement.setContent(new CDATA(localeMap.get(locale)));
+                valueElement.setContent(new CDATA(value));
                 localeBundleElement.addContent(valueElement);
             }
             localeBundleElement.setAttribute(XML_ATTRIBUTE_MODIFY_TIME, JavaHelper.toIsoDate(Instant.now()));
@@ -852,8 +851,6 @@ public class StoredConfigurationImpl implements Serializable, StoredConfiguratio
         document.getRootElement().setAttribute(XML_ATTRIBUTE_MODIFY_TIME, JavaHelper.toIsoDate(Instant.now()));
     }
 
-// -------------------------- INNER CLASSES --------------------------
-
     public void setPassword(final String password)
             throws PwmOperationalException
     {
@@ -1230,7 +1227,9 @@ public class StoredConfigurationImpl implements Serializable, StoredConfiguratio
 
         @Override
         public boolean equals(final Object o) {
-            return o instanceof StoredConfigReference && toString().equals(o);
+            return o != null
+                    && o instanceof ConfigRecordID
+                    && toString().equals(o.toString());
 
         }
 
@@ -1274,7 +1273,7 @@ public class StoredConfigurationImpl implements Serializable, StoredConfiguratio
         return changeLog.isModified();
     }
 
-    private class ChangeLog implements Serializable {
+    private class ChangeLog {
         /* values contain the _original_ toJson version of the value. */
         private Map<ConfigRecordID,String> changeLog = new LinkedHashMap<>();
 
@@ -1316,8 +1315,9 @@ public class StoredConfigurationImpl implements Serializable, StoredConfiguratio
             if (outputMap.isEmpty()) {
                 output.append("No setting changes.");
             } else {
-                for (final String keyName : outputMap.keySet()) {
-                    final String value = outputMap.get(keyName);
+                for (final Map.Entry<String, String> entry : outputMap.entrySet()) {
+                    final String keyName = entry.getKey();
+                    final String value = entry.getValue();
                     if (asHtml) {
                         output.append("<div class=\"changeLogKey\">");
                         output.append(keyName);

+ 9 - 6
server/src/main/java/password/pwm/config/value/ChallengeValue.java

@@ -99,8 +99,9 @@ public class ChallengeValue extends AbstractValue implements StoredValue {
 
     public List<Element> toXmlValues(final String valueElementName) {
         final List<Element> returnList = new ArrayList<>();
-        for (final String locale : values.keySet()) {
-            for (final ChallengeItemConfiguration value : values.get(locale)) {
+        for (final Map.Entry<String,List<ChallengeItemConfiguration>> entry : values.entrySet()) {
+            final String locale = entry.getKey();
+            for (final ChallengeItemConfiguration value : entry.getValue()) {
                 if (value != null) {
                     final Element valueElement = new Element(valueElementName);
                     valueElement.addContent(new CDATA(JsonUtil.serialize(value)));
@@ -126,8 +127,9 @@ public class ChallengeValue extends AbstractValue implements StoredValue {
         }
 
         if (values != null) {
-            for (final String localeKey : values.keySet()) {
-                for (final ChallengeItemConfiguration itemBean : values.get(localeKey)) {
+            for (final Map.Entry<String,List<ChallengeItemConfiguration>> entry : values.entrySet()) {
+                final String localeKey = entry.getKey();
+                for (final ChallengeItemConfiguration itemBean : entry.getValue()) {
                     if (itemBean != null) {
                         if (itemBean.isAdminDefined() && (itemBean.getText() == null || itemBean.getText().length() < 1)) {
                             return Collections.singletonList("admin-defined challenge must contain text (locale='" + localeKey + "')");
@@ -193,8 +195,9 @@ public class ChallengeValue extends AbstractValue implements StoredValue {
             return "No Actions";
         }
         final StringBuilder sb = new StringBuilder();
-        for (final String localeKey : values.keySet()) {
-            final List<ChallengeItemConfiguration> challengeItems = values.get(localeKey);
+        for (final Map.Entry<String,List<ChallengeItemConfiguration>> entry: values.entrySet()) {
+            final String localeKey = entry.getKey();
+            final List<ChallengeItemConfiguration> challengeItems = entry.getValue();
             sb.append("Locale: ").append(LocaleHelper.debugLabel(LocaleHelper.parseLocaleString(localeKey))).append("\n");
             for (final ChallengeItemConfiguration challengeItemBean : challengeItems) {
                 sb.append(" ChallengeItem: [AdminDefined: ").append(challengeItemBean.isAdminDefined());

+ 12 - 99
server/src/main/java/password/pwm/config/value/EmailValue.java

@@ -28,18 +28,15 @@ import password.pwm.bean.EmailItemBean;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.error.PwmOperationalException;
-import password.pwm.util.java.JsonUtil;
 import password.pwm.util.LocaleHelper;
+import password.pwm.util.java.JsonUtil;
 import password.pwm.util.secure.PwmSecurityKey;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
-import java.util.Set;
 import java.util.TreeMap;
 
 public class EmailValue extends AbstractValue implements StoredValue {
@@ -55,13 +52,13 @@ public class EmailValue extends AbstractValue implements StoredValue {
             public EmailValue fromJson(final String input)
             {
                 if (input == null) {
-                    return new EmailValue(Collections.<String, EmailItemBean>emptyMap());
+                    return new EmailValue(Collections.emptyMap());
                 } else {
                     Map<String, EmailItemBean> srcList = JsonUtil.deserialize(input,
                             new TypeToken<Map<String, EmailItemBean>>() {}
                     );
 
-                    srcList = srcList == null ? Collections.<String, EmailItemBean>emptyMap() : srcList;
+                    srcList = srcList == null ? Collections.emptyMap() : srcList;
                     srcList.remove(null);
                     return new EmailValue(Collections.unmodifiableMap(srcList));
                 }
@@ -86,93 +83,6 @@ public class EmailValue extends AbstractValue implements StoredValue {
                         }
                     }
                 }
-                // read old format values.  can be removed someday....      this code iterates through the entire settings xml document to find old format versions
-                {
-                    final Map<String, String> fromMap = new HashMap<>();
-                    final Map<String, String> subjectMap = new HashMap<>();
-                    final Map<String, String> bodyPlainMap = new HashMap<>();
-                    final Map<String, String> bodyHtmlMap = new HashMap<>();
-                    for (final Object loopSettingObj : settingElement.getParentElement().getChildren()) {
-                        final Element loopSetting = (Element) loopSettingObj;
-                        if (loopSetting.getAttribute("key") != null) {
-                            if (loopSetting.getAttribute("key").getValue().equals(
-                                    settingElement.getAttribute("key").getValue() + ".from")) {
-                                final List valueElements = loopSetting.getChildren("value");
-                                for (final Object loopValue : valueElements) {
-                                    final Element loopValueElement = (Element) loopValue;
-                                    final String value = loopValueElement.getText();
-                                    if (value != null && value.length() > 0) {
-                                        final String localeValue = settingElement.getAttribute(
-                                                "locale") == null ? "" : settingElement.getAttribute(
-                                                "locale").getValue();
-                                        fromMap.put(localeValue, value);
-                                    }
-                                }
-                            }
-                            if (loopSetting.getAttribute("key").getValue().equals(
-                                    settingElement.getAttribute("key").getValue() + ".subject")) {
-                                final List valueElements = loopSetting.getChildren("value");
-                                for (final Object loopValue : valueElements) {
-                                    final Element loopValueElement = (Element) loopValue;
-                                    final String value = loopValueElement.getText();
-                                    if (value != null && value.length() > 0) {
-                                        final String localeValue = settingElement.getAttribute(
-                                                "locale") == null ? "" : settingElement.getAttribute(
-                                                "locale").getValue();
-                                        subjectMap.put(localeValue, value);
-                                    }
-                                }
-                            }
-                            if (loopSetting.getAttribute("key").getValue().equals(
-                                    settingElement.getAttribute("key").getValue() + ".plainBody")) {
-                                final List valueElements = loopSetting.getChildren("value");
-                                for (final Object loopValue : valueElements) {
-                                    final Element loopValueElement = (Element) loopValue;
-                                    final String value = loopValueElement.getText();
-                                    if (value != null && value.length() > 0) {
-                                        final String localeValue = settingElement.getAttribute(
-                                                "locale") == null ? "" : settingElement.getAttribute(
-                                                "locale").getValue();
-                                        bodyPlainMap.put(localeValue, value);
-                                    }
-                                }
-                            }
-                            if (loopSetting.getAttribute("key").getValue().equals(
-                                    settingElement.getAttribute("key").getValue() + ".htmlBody")) {
-                                final List valueElements = loopSetting.getChildren("value");
-                                for (final Object loopValue : valueElements) {
-                                    final Element loopValueElement = (Element) loopValue;
-                                    final String value = loopValueElement.getText();
-                                    if (value != null && value.length() > 0) {
-                                        final String localeValue = settingElement.getAttribute(
-                                                "locale") == null ? "" : settingElement.getAttribute(
-                                                "locale").getValue();
-                                        bodyHtmlMap.put(localeValue, value);
-                                    }
-                                }
-                            }
-                        }
-                    }
-                    final Set<String> seenLocales = new HashSet<>();
-                    seenLocales.addAll(fromMap.keySet());
-                    seenLocales.addAll(subjectMap.keySet());
-                    seenLocales.addAll(bodyPlainMap.keySet());
-                    seenLocales.addAll(bodyHtmlMap.keySet());
-                    //final String defaultJson = PwmSetting.forKey(settingElement.getAttribute("key").getValue()).getDefaultValue(PwmSetting.Template.NOVL);
-                    //final Map<String,EmailItemBean> defaultList = gson.fromJson(defaultJson, new TypeToken<Map<String,EmailItemBean>>() {}.getType());
-                    //final EmailItemBean defaultBean = defaultList.read("");
-            /*
-            for (final String localeStr : seenLocales) {
-                values.put(localeStr,new EmailItemBean(
-                        null,
-                        fromMap.containsKey(localeStr) ? fromMap.read(localeStr) : defaultBean.getFrom(),
-                        subjectMap.containsKey(localeStr) ? subjectMap.read(localeStr) : defaultBean.getSubject(),
-                        bodyPlainMap.containsKey(localeStr)? bodyPlainMap.read(localeStr) : defaultBean.getBodyPlain(),
-                        bodyHtmlMap.containsKey(localeStr) ? bodyHtmlMap.read(localeStr) : defaultBean.getBodyHtml()
-                        ));
-            }
-            */
-                }
                 return new EmailValue(values);
             }
         };
@@ -180,8 +90,9 @@ public class EmailValue extends AbstractValue implements StoredValue {
 
     public List<Element> toXmlValues(final String valueElementName) {
         final List<Element> returnList = new ArrayList<>();
-        for (final String localeValue : values.keySet()) {
-            final EmailItemBean emailItemBean = values.get(localeValue);
+        for (final Map.Entry<String,EmailItemBean> entry : values.entrySet()) {
+            final String localeValue = entry.getKey();
+            final EmailItemBean emailItemBean = entry.getValue();
             final Element valueElement = new Element(valueElementName);
             if (localeValue.length() > 0) {
                 valueElement.setAttribute("locale",localeValue);
@@ -203,8 +114,9 @@ public class EmailValue extends AbstractValue implements StoredValue {
             }
         }
 
-        for (final String loopLocale : values.keySet()) {
-            final EmailItemBean emailItemBean = values.get(loopLocale);
+        for (final Map.Entry<String,EmailItemBean> entry : values.entrySet()) {
+            final String loopLocale = entry.getKey();
+            final EmailItemBean emailItemBean = entry.getValue();
 
             if (emailItemBean.getSubject() == null || emailItemBean.getSubject().length() < 1) {
                 return Collections.singletonList("subject field is required " + (loopLocale.length() > 0 ? " for locale " + loopLocale:""));
@@ -227,8 +139,9 @@ public class EmailValue extends AbstractValue implements StoredValue {
             return "No Email Item";
         }
         final StringBuilder sb = new StringBuilder();
-        for (final String localeKey : values.keySet()) {
-            final EmailItemBean emailItemBean = values.get(localeKey);
+        for (final Map.Entry<String,EmailItemBean> entry : values.entrySet()) {
+            final String localeKey = entry.getKey();
+            final EmailItemBean emailItemBean = entry.getValue();
             sb.append("EmailItem ").append(LocaleHelper.debugLabel(LocaleHelper.parseLocaleString(localeKey))).append(": \n");
             sb.append("  To:").append(emailItemBean.getTo()).append("\n");
             sb.append("From:").append(emailItemBean.getFrom()).append("\n");

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

@@ -22,12 +22,14 @@
 
 package password.pwm.config.value;
 
+import lombok.Value;
 import org.jdom2.Element;
 import password.pwm.PwmConstants;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.bean.ImmutableByteArray;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.logging.PwmLogger;
@@ -74,47 +76,39 @@ public class FileValue extends AbstractValue implements StoredValue {
         }
     }
 
-    public static class FileContent {
-        private byte[] contents;
+    @Value
+    public static class FileContent implements Serializable {
+        private ImmutableByteArray contents;
 
-        public FileContent(final byte[] contents)
-        {
-            this.contents = contents;
-        }
-
-        public byte[] getContents()
-        {
-            return contents;
-        }
 
         public static FileContent fromEncodedString(final String input)
                 throws IOException
         {
             final byte[] convertedBytes = StringUtil.base64Decode(input);
-            return new FileContent(convertedBytes);
+            return new FileContent(new ImmutableByteArray(convertedBytes));
         }
 
         public String toEncodedString()
                 throws IOException
         {
-            return StringUtil.base64Encode(contents, StringUtil.Base64Options.GZIP);
+            return StringUtil.base64Encode(contents.getBytes(), StringUtil.Base64Options.GZIP);
         }
 
         public String md5sum()
                 throws PwmUnrecoverableException
         {
-            return SecureEngine.hash(new ByteArrayInputStream(contents), PwmHashAlgorithm.MD5);
+            return SecureEngine.hash(new ByteArrayInputStream(contents.getBytes()), PwmHashAlgorithm.MD5);
         }
 
         public String sha1sum()
                 throws PwmUnrecoverableException
         {
-            return SecureEngine.hash(new ByteArrayInputStream(contents), PwmHashAlgorithm.SHA1);
+            return SecureEngine.hash(new ByteArrayInputStream(contents.getBytes()), PwmHashAlgorithm.SHA1);
         }
 
         public int size()
         {
-            return contents.length;
+            return contents.getBytes().length;
         }
     }
 
@@ -167,7 +161,9 @@ public class FileValue extends AbstractValue implements StoredValue {
     public List<Element> toXmlValues(final String valueElementName)
     {
         final List<Element> returnList = new ArrayList<>();
-        for (final FileInformation fileInformation : values.keySet()) {
+        for (final Map.Entry<FileInformation, FileContent> entry : this.values.entrySet()) {
+            final FileValue.FileInformation fileInformation = entry.getKey();
+            final FileContent fileContent = entry.getValue();
             final Element valueElement = new Element(valueElementName);
 
             final Element fileInformationElement = new Element("FileInformation");
@@ -175,7 +171,6 @@ public class FileValue extends AbstractValue implements StoredValue {
             valueElement.addContent(fileInformationElement);
 
             final Element fileContentElement = new Element("FileContent");
-            final FileContent fileContent = values.get(fileInformation);
             try {
                 fileContentElement.addContent(fileContent.toEncodedString());
             } catch (IOException e) {
@@ -217,15 +212,18 @@ public class FileValue extends AbstractValue implements StoredValue {
     public List<Map<String, Object>> asMetaData()
     {
         final List<Map<String, Object>> output = new ArrayList<>();
-        for (final FileInformation fileInformation : values.keySet()) {
-            final FileContent fileContent = values.get(fileInformation);
+        for (final Map.Entry<FileInformation, FileContent> entry : this.values.entrySet()) {
+            final FileValue.FileInformation fileInformation = entry.getKey();
+            final FileContent fileContent = entry.getValue();
             final Map<String, Object> details = new LinkedHashMap<>();
             details.put("name", fileInformation.getFilename());
             details.put("type", fileInformation.getFiletype());
             details.put("size", fileContent.size());
             try {
                 details.put("md5sum", fileContent.md5sum());
-            } catch (PwmUnrecoverableException e) { /* noop */ }
+            } catch (PwmUnrecoverableException e) {
+                LOGGER.trace("error generating file hash");
+            }
             output.add(details);
         }
         return output;
@@ -236,8 +234,9 @@ public class FileValue extends AbstractValue implements StoredValue {
             return Collections.emptyList();
         }
         final List<FileInfo> returnObj = new ArrayList<>();
-        for (final FileValue.FileInformation fileInformation : this.values.keySet()) {
-            final FileContent fileContent = this.values.get(fileInformation);
+        for (final Map.Entry<FileInformation, FileContent> entry : this.values.entrySet()) {
+            final FileValue.FileInformation fileInformation = entry.getKey();
+            final FileContent fileContent = entry.getValue();
             final FileInfo loopInfo = new FileInfo();
             loopInfo.name = fileInformation.getFilename();
             loopInfo.type = fileInformation.getFiletype();

+ 8 - 6
server/src/main/java/password/pwm/config/value/LocalizedStringArrayValue.java

@@ -84,8 +84,9 @@ public class LocalizedStringArrayValue extends AbstractValue implements StoredVa
 
     public List<Element> toXmlValues(final String valueElementName) {
         final List<Element> returnList = new ArrayList<>();
-        for (final String locale : values.keySet()) {
-            for (final String value : values.get(locale)) {
+        for (final Map.Entry<String,List<String>> entry : values.entrySet()) {
+            final String locale = entry.getKey();
+            for (final String value : entry.getValue()) {
                 final Element valueElement = new Element(valueElementName);
                 valueElement.addContent(new CDATA(value));
                 if (locale != null && locale.length() > 0) {
@@ -109,8 +110,8 @@ public class LocalizedStringArrayValue extends AbstractValue implements StoredVa
         }
 
         final Pattern pattern = pwmSetting.getRegExPattern();
-        for (final String locale : values.keySet()) {
-            for (final String loopValue : values.get(locale)) {
+        for (final List<String> loopValues : values.values()) {
+            for (final String loopValue : loopValues) {
                 if (loopValue != null && loopValue.length() > 0) {
                     final Matcher matcher = pattern.matcher(loopValue);
                     if (!matcher.matches()) {
@@ -129,10 +130,11 @@ public class LocalizedStringArrayValue extends AbstractValue implements StoredVa
             return "";
         }
         final StringBuilder sb = new StringBuilder();
-        for (final String localeKey : values.keySet()) {
+        for (final Map.Entry<String,List<String>> entry : values.entrySet()) {
+            final String localeKey = entry.getKey();
             if (!values.get(localeKey).isEmpty()) {
                 sb.append("Locale: ").append(LocaleHelper.debugLabel(LocaleHelper.parseLocaleString(localeKey))).append("\n");
-                for (final String value : values.get(localeKey)) {
+                for (final String value : entry.getValue()) {
                     sb.append("  ").append(value).append("\n");
                 }
             }

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

@@ -53,10 +53,10 @@ public class LocalizedStringValue extends AbstractValue implements StoredValue {
             public LocalizedStringValue fromJson(final String input)
             {
                 if (input == null) {
-                    return new LocalizedStringValue(Collections.<String, String>emptyMap());
+                    return new LocalizedStringValue(Collections.emptyMap());
                 } else {
                     Map<String, String> srcMap = JsonUtil.deserialize(input, new TypeToken<Map<String, String>>() {});
-                    srcMap = srcMap == null ? Collections.<String, String>emptyMap() : new TreeMap<>(srcMap);
+                    srcMap = srcMap == null ? Collections.emptyMap() : new TreeMap<>(srcMap);
                     return new LocalizedStringValue(Collections.unmodifiableMap(srcMap));
                 }
             }
@@ -78,10 +78,11 @@ public class LocalizedStringValue extends AbstractValue implements StoredValue {
 
     public List<Element> toXmlValues(final String valueElementName) {
         final List<Element> returnList = new ArrayList<>();
-        for (final String locale : value.keySet()) {
-            final String value = this.value.get(locale);
+        for (final Map.Entry<String,String> entry : value.entrySet()) {
+            final String locale = entry.getKey();
+            final String loopValue = entry.getValue();
             final Element valueElement = new Element(valueElementName);
-            valueElement.addContent(new CDATA(value));
+            valueElement.addContent(new CDATA(loopValue));
             if (locale != null && locale.length() > 0) {
                 valueElement.setAttribute("locale", locale);
             }
@@ -102,8 +103,7 @@ public class LocalizedStringValue extends AbstractValue implements StoredValue {
         }
 
         final Pattern pattern = pwmSetting.getRegExPattern();
-        for (final String locale : value.keySet()) {
-            final String loopValue = value.get(locale);
+        for (final String loopValue : value.values()) {
             final Matcher matcher = pattern.matcher(loopValue);
             if (loopValue.length() > 0 && !matcher.matches()) {
                 return Collections.singletonList("incorrect value format for value '" + loopValue + "'");
@@ -116,11 +116,12 @@ public class LocalizedStringValue extends AbstractValue implements StoredValue {
     @Override
     public String toDebugString(final Locale locale) {
         final StringBuilder sb = new StringBuilder();
-        for (final String localeKey : value.keySet()) {
+        for (final Map.Entry<String,String> entry : value.entrySet()) {
+            final String localeKey = entry.getKey();
             if (value.size() > 1) {
                 sb.append("Locale: ").append(LocaleHelper.debugLabel(LocaleHelper.parseLocaleString(localeKey))).append("\n");
             }
-            sb.append(" ").append(value.get(localeKey)).append("\n");
+            sb.append(" ").append(entry.getValue()).append("\n");
         }
         return sb.toString();
     }

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

@@ -144,8 +144,9 @@ public class NamedSecretValue implements StoredValue {
         }
         final List<Element> valuesElement = new ArrayList<>();
         try {
-            for (final String name : values.keySet()) {
-                final PasswordData passwordData = values.get(name).getPassword();
+            for (final Map.Entry<String,NamedSecretData> entry : values.entrySet()) {
+                final String name = entry.getKey();
+                final PasswordData passwordData = entry.getValue().getPassword();
                 final String encodedValue = SecureEngine.encryptToString(passwordData.getStringValue(), key, PwmBlockAlgorithm.CONFIG);
                 final Element newValueElement = new Element("value");
                 final Element nameElement = new Element(ELEMENT_NAME);
@@ -178,9 +179,9 @@ public class NamedSecretValue implements StoredValue {
     @Override
     public String toDebugString(final Locale locale) {
         final StringBuilder sb = new StringBuilder();
-        for (final String name : values.keySet()) {
-            final NamedSecretData existingData = values.get(name);
-            sb.append("Named password '").append(name).append("' with usage for ");
+        for (final Map.Entry<String,NamedSecretData> entry: values.entrySet()) {
+            final NamedSecretData existingData = entry.getValue();
+            sb.append("Named password '").append(entry.getKey()).append("' with usage for ");
             sb.append(StringUtil.collectionToString(existingData.getUsage(), ","));
             sb.append("\n");
 
@@ -196,8 +197,9 @@ public class NamedSecretValue implements StoredValue {
 
         try {
             final LinkedHashMap<String,NamedSecretData> copiedValues = new LinkedHashMap<>();
-            for (final String name : values.keySet()) {
-                final NamedSecretData existingData = values.get(name);
+            for (final Map.Entry<String,NamedSecretData> entry: values.entrySet()) {
+                final String name = entry.getKey();
+                final NamedSecretData existingData = entry.getValue();
                 final NamedSecretData newData = new NamedSecretData(
                         PasswordData.forStringValue(PwmConstants.LOG_REMOVED_VALUE_REPLACEMENT),
                         existingData.getUsage()

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

@@ -50,7 +50,7 @@ public class NumericValue extends AbstractValue implements StoredValue {
             {
                 final Element valueElement = settingElement.getChild("value");
                 final String value = valueElement.getText();
-                return new NumericValue(Long.valueOf(value));
+                return new NumericValue(Long.parseLong(value));
             }
         };
     }

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

@@ -86,8 +86,7 @@ public class PrivateKeyValue extends AbstractValue {
                         }
 
                         if (!certificates.isEmpty() && privateKey != null) {
-                            final X509Certificate[] certs = certificates.toArray(new X509Certificate[certificates.size()]);
-                            final PrivateKeyCertificate privateKeyCertificate = new PrivateKeyCertificate(certs, privateKey);
+                            final PrivateKeyCertificate privateKeyCertificate = new PrivateKeyCertificate(certificates, privateKey);
                             return new PrivateKeyValue(privateKeyCertificate);
                         }
                     }

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

@@ -28,7 +28,6 @@ import password.pwm.config.StoredValue;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
-import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmSecurityKey;
 
@@ -54,7 +53,6 @@ public class ValueFactory {
     }
 
     public static StoredValue fromXmlValues(final PwmSetting setting, final Element settingElement, final PwmSecurityKey key)
-            throws PwmUnrecoverableException, PwmOperationalException
     {
         try {
             final StoredValue.StoredValueFactory factory = setting.getSyntax().getStoredValueImpl();
@@ -66,7 +64,7 @@ public class ValueFactory {
                 errorMsg.append(", cause: ").append(e.getCause().getMessage());
             }
             LOGGER.error(errorMsg,e);
-            throw new PwmOperationalException(new ErrorInformation(PwmError.CONFIG_FORMAT_ERROR,errorMsg.toString()));
+            throw new IllegalStateException("unable to read xml element '" + settingElement.getName() + "' from setting '" + setting.getKey() + "' error: " + e.getMessage());
         }
     }
 }

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

@@ -22,6 +22,7 @@
 
 package password.pwm.config.value;
 
+import lombok.Value;
 import org.jdom2.CDATA;
 import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
@@ -78,16 +79,9 @@ public class VerificationMethodValue extends AbstractValue implements StoredValu
         }
     }
 
-    public static class VerificationMethodSetting {
-        private EnabledState enabledState = EnabledState.disabled;
-
-        public VerificationMethodSetting(final EnabledState enabledState) {
-            this.enabledState = enabledState;
-        }
-
-        public EnabledState getEnabledState() {
-            return enabledState;
-        }
+    @Value
+    public static class VerificationMethodSetting implements Serializable {
+        private final EnabledState enabledState;
     }
 
     public VerificationMethodValue() {

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

@@ -38,6 +38,7 @@ import java.io.Serializable;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -74,7 +75,7 @@ public class X509CertificateValue extends AbstractValue implements StoredValue {
         if (certificates == null) {
             throw new NullPointerException("certificates cannot be null");
         }
-        this.certificates = certificates;
+        this.certificates = Arrays.copyOf(certificates, certificates.length);
     }
 
     public boolean hasCertificates() {
@@ -106,7 +107,7 @@ public class X509CertificateValue extends AbstractValue implements StoredValue {
 
     @Override
     public Object toNativeObject() {
-        return certificates;
+        return certificates == null ? null : Arrays.copyOf(certificates, certificates.length);
     }
 
     @Override

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

@@ -31,6 +31,7 @@ import password.pwm.util.java.JsonUtil;
 
 import java.io.Serializable;
 import java.security.cert.X509Certificate;
+import java.util.List;
 import java.util.Map;
 
 @Getter
@@ -52,7 +53,7 @@ public class ActionConfiguration implements Serializable {
     private Map<String,String> headers;
     private String url;
     private String body;
-    private X509Certificate[] certificates;
+    private List<X509Certificate> certificates;
 
 
     private LdapMethod ldapMethod = LdapMethod.replace;
@@ -98,7 +99,7 @@ public class ActionConfiguration implements Serializable {
         }
     }
 
-    public ActionConfiguration copyWithNewCertificate(final X509Certificate[] certificates) {
+    public ActionConfiguration copyWithNewCertificate(final List<X509Certificate> certificates) {
         final ActionConfiguration clone = JsonUtil.cloneUsingJson(this, ActionConfiguration.class);
         clone.certificates = certificates;
         return clone;

+ 3 - 5
server/src/main/java/password/pwm/config/value/data/FormConfiguration.java

@@ -180,8 +180,6 @@ public class FormConfiguration implements Serializable {
         regexErrors = Collections.singletonMap("","");
     }
 
-// --------------------- GETTER / SETTER METHODS ---------------------
-
     public String getName() {
         return name;
     }
@@ -254,7 +252,6 @@ public class FormConfiguration implements Serializable {
         return Collections.unmodifiableMap(selectOptions);
     }
 
-// ------------------------ CANONICAL METHODS ------------------------
     public boolean equals(final Object o) {
         if (this == o) {
             return true;
@@ -380,9 +377,10 @@ public class FormConfiguration implements Serializable {
 
         if (this.getType() == Type.select) {
             if (this.getSelectOptions() != null) {
-                for (final String key : selectOptions.keySet()) {
+                for (final Map.Entry<String,String> entry : selectOptions.entrySet()) {
+                    final String key = entry.getKey();
                     if (value.equals(key)) {
-                        final String displayValue = selectOptions.get(key);
+                        final String displayValue = entry.getValue();
                         if (!StringUtil.isEmpty(displayValue)) {
                             return displayValue;
                         }

+ 2 - 1
server/src/main/java/password/pwm/config/value/data/RemoteWebServiceConfiguration.java

@@ -27,6 +27,7 @@ import lombok.Setter;
 
 import java.io.Serializable;
 import java.security.cert.X509Certificate;
+import java.util.List;
 import java.util.Map;
 
 @Getter
@@ -46,5 +47,5 @@ public class RemoteWebServiceConfiguration implements Serializable {
     private Map<String,String> headers;
     private String url;
     private String body;
-    private X509Certificate[] certificates;
+    private List<X509Certificate> certificates;
 }

+ 1 - 7
server/src/main/java/password/pwm/error/ErrorInformation.java

@@ -37,8 +37,6 @@ import java.util.Locale;
  * to use when presenting error messages to users.
  */
 public class ErrorInformation implements Serializable {
-// ------------------------------ FIELDS ------------------------------
-
     private final PwmError error;
     private final String detailedErrorMsg;
     private final String userStrOverride;
@@ -81,8 +79,6 @@ public class ErrorInformation implements Serializable {
         this.fieldValues = fields == null ? new String[0] : fields;
     }
 
-// --------------------- GETTER / SETTER METHODS ---------------------
-
     public String getDetailedErrorMsg() {
         return detailedErrorMsg;
     }
@@ -91,10 +87,8 @@ public class ErrorInformation implements Serializable {
         return error;
     }
 
-// -------------------------- OTHER METHODS --------------------------
-
     public String[] getFieldValues() {
-        return fieldValues;
+        return fieldValues == null ? null : Arrays.copyOf(fieldValues, fieldValues.length);
     }
 
     public String toDebugStr() {

+ 0 - 2
server/src/main/java/password/pwm/error/PwmDataValidationException.java

@@ -28,8 +28,6 @@ package password.pwm.error;
  * @author Jason D. Rivard
  */
 public class PwmDataValidationException extends PwmOperationalException {
-// ------------------------------ FIELDS ------------------------------
-
     public PwmDataValidationException(final ErrorInformation error) {
         super(error);
     }

+ 0 - 2
server/src/main/java/password/pwm/error/PwmException.java

@@ -42,8 +42,6 @@ public abstract class PwmException extends Exception {
         this.errorInformation = new ErrorInformation(error, detailedErrorMsg);
     }
 
-    // --------------------- GETTER / SETTER METHODS ---------------------
-
     public ErrorInformation getErrorInformation() {
         return errorInformation;
     }

+ 3 - 3
server/src/main/java/password/pwm/health/ApplianceStatusChecker.java

@@ -81,9 +81,9 @@ public class ApplianceStatusChecker implements HealthChecker {
         final String url = figureUrl(pwmApplication);
         final Map<String,String> requestHeaders = Collections.singletonMap("sspr-authorization-token", getApplianceAccessToken(pwmApplication));
 
-        final PwmHttpClientConfiguration pwmHttpClientConfiguration = new PwmHttpClientConfiguration.Builder()
-                .setPromiscuous(true)
-                .create();
+        final PwmHttpClientConfiguration pwmHttpClientConfiguration = PwmHttpClientConfiguration.builder()
+                .promiscuous(true)
+                .build();
 
         final PwmHttpClient pwmHttpClient = new PwmHttpClient(pwmApplication, SessionLabel.HEALTH_SESSION_LABEL, pwmHttpClientConfiguration);
         final PwmHttpClientRequest pwmHttpClientRequest = new PwmHttpClientRequest(HttpMethod.GET, url, null, requestHeaders);

+ 9 - 4
server/src/main/java/password/pwm/health/CertificateChecker.java

@@ -69,13 +69,13 @@ public class CertificateChecker implements HealthChecker {
         for (final PwmSetting setting : PwmSetting.values()) {
             if (setting.getSyntax() == PwmSettingSyntax.X509CERT && !setting.getCategory().hasProfiles()) {
                 if (setting != PwmSetting.LDAP_SERVER_CERTS) {
-                    final X509Certificate[] certs = configuration.readSettingAsCertificate(setting);
+                    final List<X509Certificate> certs = configuration.readSettingAsCertificate(setting);
                     returnList.addAll(doHealthCheck(configuration,setting,null,certs));
                 }
             }
         }
         for (final LdapProfile ldapProfile : configuration.getLdapProfiles().values()) {
-           final X509Certificate[] certificates = configuration.getLdapProfiles().get(ldapProfile.getIdentifier()).readSettingAsCertificate(PwmSetting.LDAP_SERVER_CERTS);
+           final List<X509Certificate> certificates = configuration.getLdapProfiles().get(ldapProfile.getIdentifier()).readSettingAsCertificate(PwmSetting.LDAP_SERVER_CERTS);
             returnList.addAll(doHealthCheck(configuration,PwmSetting.LDAP_SERVER_CERTS,ldapProfile.getIdentifier(),certificates));
         }
         return Collections.unmodifiableList(returnList);
@@ -93,7 +93,7 @@ public class CertificateChecker implements HealthChecker {
                 if (pwmSetting != null && pwmSetting.getSyntax() == PwmSettingSyntax.ACTION) {
                     final ActionValue value = (ActionValue)storedConfiguration.readSetting(pwmSetting, storedConfigReference.getProfileID());
                     for (final ActionConfiguration actionConfiguration : value.toNativeObject()) {
-                        final X509Certificate[] certificates = actionConfiguration.getCertificates();
+                        final List<X509Certificate> certificates = actionConfiguration.getCertificates();
                         returnList.addAll(doHealthCheck(configuration, pwmSetting, storedConfigReference.getProfileID(), certificates));
                     }
                 }
@@ -102,7 +102,12 @@ public class CertificateChecker implements HealthChecker {
         return Collections.unmodifiableList(returnList);
     }
 
-    private static List<HealthRecord> doHealthCheck(final Configuration configuration, final PwmSetting setting, final String profileID, final X509Certificate[] certificates) {
+    private static List<HealthRecord> doHealthCheck(
+            final Configuration configuration,
+            final PwmSetting setting,
+            final String profileID,
+            final List<X509Certificate> certificates
+    ) {
         final long warnDurationMs = 1000 * Long.parseLong(configuration.readAppProperty(AppProperty.HEALTH_CERTIFICATE_WARN_SECONDS));
 
         if (certificates != null) {

+ 5 - 1
server/src/main/java/password/pwm/health/HealthRecord.java

@@ -22,6 +22,7 @@
 
 package password.pwm.health;
 
+import lombok.EqualsAndHashCode;
 import password.pwm.config.Configuration;
 import password.pwm.ws.server.rest.bean.HealthData;
 
@@ -31,7 +32,8 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Locale;
 
-public class HealthRecord implements Serializable,Comparable<HealthRecord> {
+@EqualsAndHashCode
+public class HealthRecord implements Serializable, Comparable<HealthRecord> {
     private final HealthStatus status;
 
     // new fields
@@ -150,6 +152,8 @@ public class HealthRecord implements Serializable,Comparable<HealthRecord> {
         return 0;
     }
 
+
+
     public List<HealthRecord> singletonList() {
         return Collections.singletonList(this);
     }

+ 7 - 5
server/src/main/java/password/pwm/health/LDAPStatusChecker.java

@@ -93,10 +93,11 @@ public class LDAPStatusChecker implements HealthChecker {
         final List<HealthRecord> returnRecords = new ArrayList<>();
         final Map<String,LdapProfile> ldapProfiles = pwmApplication.getConfig().getLdapProfiles();
 
-        for (final String profileID : ldapProfiles.keySet()) {
+        for (final Map.Entry<String, LdapProfile> entry : ldapProfiles.entrySet()) {
+            final String profileID = entry.getKey();
             final List<HealthRecord> profileRecords = new ArrayList<>();
             profileRecords.addAll(
-                    checkBasicLdapConnectivity(pwmApplication, config, ldapProfiles.get(profileID), true));
+                    checkBasicLdapConnectivity(pwmApplication, config, entry.getValue(), true));
 
             if (profileRecords.isEmpty()) {
                 profileRecords.addAll(checkLdapServerUrls(config, ldapProfiles.get(profileID)));
@@ -561,9 +562,10 @@ public class LDAPStatusChecker implements HealthChecker {
 
         if (discoveredVendors.size() >= 2) {
             final StringBuilder vendorMsg = new StringBuilder();
-            for (final Iterator<String> iterator = replicaVendorMap.keySet().iterator(); iterator.hasNext(); ) {
-                final String key = iterator.next();
-                vendorMsg.append(key).append("=").append(replicaVendorMap.get(key).toString());
+            for (final Iterator<Map.Entry<String,ChaiProvider.DIRECTORY_VENDOR>> iterator = replicaVendorMap.entrySet().iterator(); iterator.hasNext(); ) {
+                final Map.Entry<String,ChaiProvider.DIRECTORY_VENDOR> entry = iterator.next();
+                final String key = entry.getKey();
+                vendorMsg.append(key).append("=").append(entry.getValue().toString());
                 if (iterator.hasNext()) {
                     vendorMsg.append(", ");
                 }

+ 4 - 14
server/src/main/java/password/pwm/http/ContextManager.java

@@ -53,15 +53,14 @@ import java.util.Timer;
 import java.util.TimerTask;
 
 public class ContextManager implements Serializable {
-// ------------------------------ FIELDS ------------------------------
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(ContextManager.class);
 
-    private ServletContext servletContext;
-    private Timer taskMaster;
+    private transient ServletContext servletContext;
+    private transient Timer taskMaster;
 
     private transient PwmApplication pwmApplication;
-    private ConfigurationReader configReader;
+    private transient ConfigurationReader configReader;
     private ErrorInformation startupErrorInformation;
 
     private volatile boolean restartRequestedFlag = false;
@@ -78,7 +77,6 @@ public class ContextManager implements Serializable {
         this.contextPath = servletContext.getContextPath();
     }
 
-    // -------------------------- STATIC METHODS --------------------------
 
     public static PwmApplication getPwmApplication(final HttpServletRequest request) throws PwmUnrecoverableException {
         return getPwmApplication(request.getServletContext());
@@ -112,8 +110,6 @@ public class ContextManager implements Serializable {
         return (ContextManager) theManager;
     }
 
-// --------------------- GETTER / SETTER METHODS ---------------------
-
     public PwmApplication getPwmApplication()
             throws PwmUnrecoverableException
     {
@@ -129,8 +125,6 @@ public class ContextManager implements Serializable {
         return pwmApplication;
     }
 
-// -------------------------- OTHER METHODS --------------------------
-
     public void initialize() {
 
         try {
@@ -163,11 +157,7 @@ public class ContextManager implements Serializable {
             configReader.getStoredConfiguration().lock();
             configuration = configReader.getConfiguration();
 
-            if (configReader == null) {
-                mode = startupErrorInformation == null ? PwmApplicationMode.ERROR : PwmApplicationMode.ERROR;
-            } else {
-                mode = startupErrorInformation == null ? configReader.getConfigMode() : PwmApplicationMode.ERROR;
-            }
+            mode = startupErrorInformation == null ? configReader.getConfigMode() : PwmApplicationMode.ERROR;
 
             if (startupErrorInformation == null) {
                 startupErrorInformation = configReader.getConfigFileError();

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

@@ -19,15 +19,16 @@ public enum HttpContentType {
     ;
 
     private final String mimeType;
-    private final Charset charset;
+    private final String charset;
 
     HttpContentType(final String mimeType, final Charset charset) {
         this.mimeType = mimeType;
-        this.charset = charset;
+        this.charset = charset.name();
     }
 
     HttpContentType(final String mimeType) {
-        this(mimeType, null);
+        this.mimeType = mimeType;
+        this.charset = null;
     }
 
     public String getHeaderValue() {

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

@@ -23,6 +23,7 @@
 package password.pwm.http;
 
 import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
 import lombok.Getter;
 import password.pwm.AppProperty;
 import password.pwm.Permission;
@@ -135,6 +136,7 @@ public class IdleTimeoutCalculator {
 
     @Getter
     @AllArgsConstructor
+    @EqualsAndHashCode
     static class MaxIdleTimeoutResult implements Comparable<MaxIdleTimeoutResult> {
         private final String reason;
         private final TimeDuration idleTimeout;

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

@@ -134,13 +134,14 @@ public abstract class PwmHttpRequestWrapper {
 
         final Map<String, String> outputMap = new LinkedHashMap<>();
         if (inputMap != null) {
-            for (final String key : inputMap.keySet()) {
+            for (final Map.Entry<String,String> entry : inputMap.entrySet()) {
+                final String key = entry.getKey();
                 if (key != null) {
                     final boolean passwordType = key.toLowerCase().contains("password");
                     String value;
                     value = bypassInputValidation
-                            ? inputMap.get(key)
-                            : Validator.sanitizeInputValue(configuration, inputMap.get(key), maxLength);
+                            ? entry.getValue()
+                            : Validator.sanitizeInputValue(configuration, entry.getValue(), maxLength);
                     value = passwordType && passwordTrim ? value.trim() : value;
                     value = !passwordType && trim ? value.trim() : value;
 
@@ -166,19 +167,20 @@ public abstract class PwmHttpRequestWrapper {
 
         final Map<String, Object> outputMap = new LinkedHashMap<>();
         if (inputMap != null) {
-            for (final String key : inputMap.keySet()) {
+            for (final Map.Entry<String,Object> entry : inputMap.entrySet()) {
+                final String key = entry.getKey();
                 if (key != null) {
                     final boolean passwordType = key.toLowerCase().contains("password");
                     final Object value;
                     if (inputMap.get(key) instanceof String) {
                         String stringValue = bypassInputValidation
-                                ? (String)inputMap.get(key) :
-                                Validator.sanitizeInputValue(configuration, (String)inputMap.get(key), maxLength);
+                                ? (String)entry.getValue() :
+                                Validator.sanitizeInputValue(configuration, (String)entry.getValue(), maxLength);
                         stringValue = passwordType && passwordTrim ? stringValue.trim() : stringValue;
                         stringValue = !passwordType && trim ? stringValue.trim() : stringValue;
                         value = stringValue;
                     } else {
-                        value = inputMap.get(key);
+                        value = entry.getValue();
                     }
 
                     final String sanitizedName = Validator.sanitizeInputValue(configuration, key, maxLength);

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

@@ -22,6 +22,7 @@
 
 package password.pwm.http;
 
+import lombok.Value;
 import org.apache.commons.fileupload.FileItemIterator;
 import org.apache.commons.fileupload.FileItemStream;
 import org.apache.commons.fileupload.servlet.ServletFileUpload;
@@ -39,6 +40,7 @@ import password.pwm.config.value.data.FormConfiguration;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.http.bean.ImmutableByteArray;
 import password.pwm.http.servlet.PwmServletDefinition;
 import password.pwm.http.servlet.command.CommandServlet;
 import password.pwm.ldap.UserInfo;
@@ -66,22 +68,21 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
-public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
-// ------------------------------ FIELDS ------------------------------
+public class PwmRequest extends PwmHttpRequestWrapper {
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(PwmRequest.class);
 
     private static final Set<String> HTTP_PARAM_DEBUG_STRIP_VALUES =
-            Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new String[] {
+            Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
                     "password",
                     PwmConstants.PARAM_TOKEN,
-                    PwmConstants.PARAM_RESPONSE_PREFIX,
-            })));
+                    PwmConstants.PARAM_RESPONSE_PREFIX))
+            );
 
     private static final Set<String> HTTP_HEADER_DEBUG_STRIP_VALUES =
-            Collections.unmodifiableSet(new HashSet<>(Arrays.asList(new String[] {
-                    HttpHeader.Authorization.getHttpName(),
-            })));
+            Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+                    HttpHeader.Authorization.getHttpName()))
+            );
 
     private final PwmResponse pwmResponse;
     private transient PwmApplication pwmApplication;
@@ -257,7 +258,7 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
                     final FileUploadItem fileUploadItem = new FileUploadItem(
                             item.getName(),
                             item.getContentType(),
-                            outputFile
+                            new ImmutableByteArray(outputFile)
                     );
                     returnObj.put(item.getFieldName(),fileUploadItem);
                 }
@@ -268,36 +269,11 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
         return Collections.unmodifiableMap(returnObj);
     }
 
+    @Value
     public static class FileUploadItem {
         private final String name;
         private final String type;
-        private final byte[] content;
-
-        public FileUploadItem(
-                final String name,
-                final String type,
-                final byte[] content
-        )
-        {
-            this.name = name;
-            this.type = type;
-            this.content = content;
-        }
-
-        public String getName()
-        {
-            return name;
-        }
-
-        public String getType()
-        {
-            return type;
-        }
-
-        public byte[] getContent()
-        {
-            return content;
-        }
+        private final ImmutableByteArray content;
     }
 
     public UserIdentity getUserInfoIfLoggedIn() {
@@ -546,7 +522,6 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
     }
 
     public String getURLwithQueryString() throws PwmUnrecoverableException {
-        final HttpServletRequest req = this.getHttpServletRequest();
         return PwmURL.appendAndEncodeUrlParameters(getURLwithoutQueryString(), readParametersAsMap());
     }
 
@@ -586,12 +561,14 @@ public class PwmRequest extends PwmHttpRequestWrapper implements Serializable {
     private static String debugOutputMapToString(
             final Map<String,List<String>> input,
             final Collection<String> stripValues
-
-    ) {
+    )
+    {
         final String LINE_SEPARATOR = "\n";
+
         final StringBuilder sb = new StringBuilder();
-        for (final String paramName : input.keySet()) {
-            for (final String paramValue : input.get(paramName)) {
+        for (final Map.Entry<String,List<String>> entry : input.entrySet()) {
+            final String paramName = entry.getKey();
+            for (final String paramValue : entry.getValue()) {
                 sb.append("  ").append(paramName).append("=");
                 boolean strip = false;
                 for (final String stripValue : stripValues) {

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

@@ -22,6 +22,7 @@
 
 package password.pwm.http;
 
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import password.pwm.PwmApplication;
 import password.pwm.config.Configuration;
@@ -84,6 +85,7 @@ public class PwmResponse extends PwmHttpResponseWrapper {
         this.pwmRequest = pwmRequest;
     }
 
+    @SuppressFBWarnings("DE_MIGHT_IGNORE") // its okay to disappear the exception during logging
     public void forwardToJsp(
             final JspUrl jspURL
     )

+ 17 - 14
server/src/main/java/password/pwm/http/PwmSession.java

@@ -46,6 +46,7 @@ import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmRandom;
 
 import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.io.ObjectOutputStream;
 import java.io.Serializable;
 import java.math.BigInteger;
@@ -60,14 +61,13 @@ import java.util.Map;
  * @author Jason D. Rivard
  */
 public class PwmSession implements Serializable {
-// ------------------------------ FIELDS ------------------------------
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(PwmSession.class);
 
     private final LocalSessionStateBean sessionStateBean;
 
     private LoginInfoBean loginInfoBean;
-    private UserInfo userInfoBean;
+    private transient UserInfo userInfo;
     private UserSessionDataCacheBean userSessionDataCacheBean;
 
     private static final Object CREATION_LOCK = new Object();
@@ -100,7 +100,9 @@ public class PwmSession implements Serializable {
             String nextID = pwmApplication.getStatisticsManager().getStatBundleForKey(StatisticsManager.KEY_CUMULATIVE).getStatistic(Statistic.HTTP_SESSIONS);
             try {
                 nextID = new BigInteger(nextID).toString();
-            } catch (Exception e) { /* ignore */ }
+            } catch (NumberFormatException e) {
+                LOGGER.debug(this, "error generating sessionID: " + e.getMessage(),e);
+            }
             this.getSessionStateBean().setSessionID(nextID);
         }
 
@@ -131,14 +133,14 @@ public class PwmSession implements Serializable {
         if (!isAuthenticated()) {
             throw new IllegalStateException("attempt to read user info bean, but session not authenticated");
         }
-        if (userInfoBean == null) {
-            userInfoBean = UserInfoBean.builder().build();
+        if (userInfo == null) {
+            userInfo = UserInfoBean.builder().build();
         }
-        return userInfoBean;
+        return userInfo;
     }
 
-    public void setUserInfoBean(final UserInfo userInfoBean) {
-        this.userInfoBean = userInfoBean;
+    public void setUserInfo(final UserInfo userInfo) {
+        this.userInfo = userInfo;
     }
 
     public void reloadUserInfoBean(final PwmApplication pwmApplication) throws PwmUnrecoverableException
@@ -165,7 +167,7 @@ public class PwmSession implements Serializable {
             );
         }
 
-        setUserInfoBean(userInfo);
+        setUserInfo(userInfo);
     }
 
     public LoginInfoBean getLoginInfoBean() {
@@ -242,7 +244,7 @@ public class PwmSession implements Serializable {
             pwmRequest.getHttpServletRequest().setAttribute(PwmConstants.SESSION_ATTR_BEANS,null);
         }
 
-        userInfoBean = null;
+        userInfo = null;
         loginInfoBean = null;
         userSessionDataCacheBean = null;
     }
@@ -306,12 +308,13 @@ public class PwmSession implements Serializable {
     }
 
     public int size() {
-        try {
-            final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+        try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
             final ObjectOutputStream out = new ObjectOutputStream(byteArrayOutputStream);
             out.writeObject(this);
-            return byteArrayOutputStream.size();
-        } catch (Exception e) {
+            out.flush();
+            return byteArrayOutputStream.toByteArray().length;
+        } catch (IOException e) {
+            LOGGER.debug(this, "exception while estimating session size: " + e.getMessage());
             return 0;
         }
     }

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

@@ -257,8 +257,7 @@ public class PwmURL {
         output.append(inputUrl == null ? "" : inputUrl);
 
         if (parameters != null) {
-            for (final String key : parameters.keySet()) {
-                final String value = parameters.get(key);
+            parameters.forEach((key, value) -> {
                 final String encodedValue = value == null
                         ? ""
                         : StringUtil.urlEncode(value);
@@ -267,7 +266,7 @@ public class PwmURL {
                 output.append(key);
                 output.append("=");
                 output.append(encodedValue);
-            }
+            });
         }
 
         if (output.charAt(0) == '?' || output.charAt(0) == '&') {

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

@@ -29,24 +29,23 @@ import com.novell.ldapchai.provider.ChaiProvider;
 import password.pwm.Permission;
 import password.pwm.PwmApplication;
 import password.pwm.bean.UserIdentity;
-import password.pwm.ldap.UserInfo;
 import password.pwm.config.PwmSetting;
-import password.pwm.config.value.data.UserPermission;
 import password.pwm.config.profile.DeleteAccountProfile;
 import password.pwm.config.profile.HelpdeskProfile;
 import password.pwm.config.profile.Profile;
 import password.pwm.config.profile.ProfileType;
 import password.pwm.config.profile.UpdateAttributesProfile;
+import password.pwm.config.value.data.UserPermission;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.ldap.LdapOperationsHelper;
 import password.pwm.ldap.LdapPermissionTester;
+import password.pwm.ldap.UserInfo;
 import password.pwm.util.PasswordData;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroMachine;
 
-import java.io.Serializable;
 import java.util.List;
 
 /**
@@ -56,7 +55,7 @@ import java.util.List;
  *
  * @author Jason D. Rivard
  */
-public class SessionManager implements Serializable {
+public class SessionManager {
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(SessionManager.class);
 

+ 0 - 3
server/src/main/java/password/pwm/http/bean/ChangePasswordBean.java

@@ -64,9 +64,6 @@ public class ChangePasswordBean extends PwmSessionBean {
     @SerializedName("mc")
     private Instant changePasswordMaxCompletion;
 
-
-// --------------------- GETTER / SETTER METHODS ---------------------
-
     public boolean isAgreementPassed() {
         return agreementPassed;
     }

+ 2 - 1
server/src/main/java/password/pwm/http/bean/ConfigGuideBean.java

@@ -33,6 +33,7 @@ import password.pwm.http.servlet.configguide.GuideStep;
 import java.security.cert.X509Certificate;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -42,7 +43,7 @@ public class ConfigGuideBean extends PwmSessionBean {
 
     private GuideStep step = GuideStep.START;
     private final Map<ConfigGuideFormField,String> formData = new HashMap<>(ConfigGuideForm.defaultForm());
-    private X509Certificate[] ldapCertificates;
+    private List<X509Certificate> ldapCertificates;
     private boolean certsTrustedbyKeystore = false;
     private boolean useConfiguredCerts = false;
     private FileValue databaseDriver = null;

+ 1 - 1
server/src/main/java/password/pwm/http/bean/ConfigManagerBean.java

@@ -29,7 +29,7 @@ import java.util.Collections;
 import java.util.Set;
 
 public class ConfigManagerBean extends PwmSessionBean {
-    private StoredConfigurationImpl storedConfiguration;
+    private transient StoredConfigurationImpl storedConfiguration;
     private boolean passwordVerified;
     private boolean configUnlockedWarningShown;
 

+ 0 - 1
server/src/main/java/password/pwm/http/bean/ForgottenPasswordBean.java

@@ -42,7 +42,6 @@ import java.util.Set;
  * @author Jason D. Rivard
  */
 public class ForgottenPasswordBean extends PwmSessionBean {
-// ------------------------------ FIELDS ------------------------------
 
     @SerializedName("u")
     private UserIdentity userIdentity;

+ 4 - 7
server/src/main/java/password/pwm/http/bean/GuestRegistrationBean.java

@@ -27,9 +27,9 @@ import password.pwm.config.option.SessionBeanMode;
 import password.pwm.http.servlet.GuestRegistrationServlet;
 import password.pwm.util.FormMap;
 
+import java.time.Instant;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Date;
 import java.util.HashSet;
 import java.util.Set;
 
@@ -37,15 +37,12 @@ import java.util.Set;
  * @author Jason D. Rivard, Menno Pieters
  */
 public class GuestRegistrationBean extends PwmSessionBean {
-// ------------------------------ FIELDS ------------------------------
 
     private UserIdentity updateUserIdentity;
-    private Date updateUserExpirationDate;
+    private Instant updateUserExpirationDate;
     private FormMap formValues = new FormMap();
     private GuestRegistrationServlet.Page currentPage = GuestRegistrationServlet.Page.create;
 
-// --------------------- GETTER / SETTER METHODS ---------------------
-
     public UserIdentity getUpdateUserIdentity() {
         return updateUserIdentity;
     }
@@ -54,11 +51,11 @@ public class GuestRegistrationBean extends PwmSessionBean {
         this.updateUserIdentity = updateUserIdentity;
     }
 
-    public Date getUpdateUserExpirationDate() {
+    public Instant getUpdateUserExpirationDate() {
         return updateUserExpirationDate;
     }
 
-    public void setUpdateUserExpirationDate(final Date updateUserExpirationDate) {
+    public void setUpdateUserExpirationDate(final Instant updateUserExpirationDate) {
         this.updateUserExpirationDate = updateUserExpirationDate;
     }
 

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

@@ -0,0 +1,16 @@
+package password.pwm.http.bean;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+public class ImmutableByteArray implements Serializable {
+    private final byte[] bytes;
+
+    public ImmutableByteArray(final byte[] bytes) {
+        this.bytes = bytes == null ? null : Arrays.copyOf(bytes, bytes.length);
+    }
+
+    public byte[] getBytes() {
+        return bytes == null ? null : Arrays.copyOf(bytes, bytes.length);
+    }
+}

+ 10 - 7
server/src/main/java/password/pwm/http/client/PwmHttpClient.java

@@ -94,7 +94,7 @@ public class PwmHttpClient {
     public PwmHttpClient(final PwmApplication pwmApplication, final SessionLabel sessionLabel) {
         this.pwmApplication = pwmApplication;
         this.sessionLabel = sessionLabel;
-        this.pwmHttpClientConfiguration = new PwmHttpClientConfiguration.Builder().setCertificate(null).create();
+        this.pwmHttpClientConfiguration = PwmHttpClientConfiguration.builder().certificates(null).build();
     }
 
     public PwmHttpClient(final PwmApplication pwmApplication, final SessionLabel sessionLabel, final PwmHttpClientConfiguration pwmHttpClientConfiguration) {
@@ -106,7 +106,7 @@ public class PwmHttpClient {
     public static HttpClient getHttpClient(final Configuration configuration)
             throws PwmUnrecoverableException
     {
-        return getHttpClient(configuration, new PwmHttpClientConfiguration.Builder().setCertificate(null).create());
+        return getHttpClient(configuration, PwmHttpClientConfiguration.builder().certificates(null).build());
     }
 
     public static HttpClient getHttpClient(final Configuration configuration, final PwmHttpClientConfiguration pwmHttpClientConfiguration)
@@ -178,8 +178,10 @@ public class PwmHttpClient {
             msg.append(" (no body)");
         }
         msg.append("\n");
-        for (final String key : headers.keySet()) {
-            msg.append("  header: ").append(key).append("=").append(headers.get(key)).append("\n");
+        for (final Map.Entry<String,String> entry : headers.entrySet()) {
+            final String key = entry.getKey();
+            final String value = entry.getValue();
+            msg.append("  header: ").append(key).append("=").append(value).append("\n");
         }
         if (body != null && !body.isEmpty()) {
             msg.append("  body: ").append(body);
@@ -196,8 +198,9 @@ public class PwmHttpClient {
         }
     }
 
-    PwmHttpClientResponse makeRequestImpl(final PwmHttpClientRequest clientRequest)
-            throws IOException, URISyntaxException, PwmUnrecoverableException {
+    private PwmHttpClientResponse makeRequestImpl(final PwmHttpClientRequest clientRequest)
+            throws IOException, URISyntaxException, PwmUnrecoverableException
+    {
         final Instant startTime = Instant.now();
         final int counter = classCounter++;
 
@@ -280,7 +283,7 @@ public class PwmHttpClient {
         return httpClient.execute(httpRequest);
     }
 
-    protected static SSLContext promiscuousSSLContext() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
+    private static SSLContext promiscuousSSLContext() throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException {
         return new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
             public boolean isTrusted(final X509Certificate[] arg0, final String arg1) throws CertificateException {
                 return true;

+ 7 - 33
server/src/main/java/password/pwm/http/client/PwmHttpClientConfiguration.java

@@ -22,41 +22,15 @@
 
 package password.pwm.http.client;
 
+import lombok.Builder;
+import lombok.Value;
+
 import java.security.cert.X509Certificate;
+import java.util.List;
 
+@Value
+@Builder
 public class PwmHttpClientConfiguration {
-    private X509Certificate[] certificates;
+    private List<X509Certificate> certificates;
     private boolean promiscuous;
-
-    private PwmHttpClientConfiguration(final X509Certificate[] certificate, final boolean promiscuous) {
-        this.certificates = certificate;
-        this.promiscuous = promiscuous;
-    }
-
-    public X509Certificate[] getCertificates() {
-        return certificates;
-    }
-
-    public boolean isPromiscuous() {
-        return promiscuous;
-    }
-
-    public static class Builder {
-        private X509Certificate[] certificate;
-        private boolean promiscuous;
-
-        public Builder setCertificate(final X509Certificate[] certificate) {
-            this.certificate = certificate;
-            return this;
-        }
-
-        public Builder setPromiscuous(final boolean promiscuous) {
-            this.promiscuous = promiscuous;
-            return this;
-        }
-
-        public PwmHttpClientConfiguration create() {
-            return new PwmHttpClientConfiguration(certificate, promiscuous);
-        }
-    }
 }

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

@@ -27,6 +27,7 @@ import password.pwm.http.HttpMethod;
 import java.io.Serializable;
 import java.security.cert.X509Certificate;
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 
 public class PwmHttpClientRequest implements Serializable {
@@ -34,7 +35,7 @@ public class PwmHttpClientRequest implements Serializable {
     private final String url;
     private final String body;
     private final Map<String,String> headers;
-    private final X509Certificate[] trustedCertificates;
+    private final List<X509Certificate> trustedCertificates;
 
     public PwmHttpClientRequest(
             final HttpMethod method,
@@ -45,7 +46,7 @@ public class PwmHttpClientRequest implements Serializable {
         this.method = method;
         this.url = url;
         this.body = body;
-        this.headers = headers == null ? Collections.<String,String>emptyMap() : Collections.unmodifiableMap(headers);
+        this.headers = headers == null ? Collections.emptyMap() : Collections.unmodifiableMap(headers);
         this.trustedCertificates = null;
     }
 
@@ -54,12 +55,12 @@ public class PwmHttpClientRequest implements Serializable {
             final String url,
             final String body,
             final Map<String, String> headers,
-            final X509Certificate[] trustedCertificates
+            final List<X509Certificate> trustedCertificates
     ) {
         this.method = method;
         this.url = url;
         this.body = body;
-        this.headers = headers == null ? Collections.<String,String>emptyMap() : Collections.unmodifiableMap(headers);
+        this.headers = headers == null ? Collections.emptyMap() : Collections.unmodifiableMap(headers);
         this.trustedCertificates = trustedCertificates;
     }
 
@@ -79,7 +80,7 @@ public class PwmHttpClientRequest implements Serializable {
         return headers;
     }
 
-    public X509Certificate[] getTrustedCertificates() {
+    public List<X509Certificate> getTrustedCertificates() {
         return trustedCertificates;
     }
 

+ 0 - 1
server/src/main/java/password/pwm/http/filter/AuthenticationFilter.java

@@ -76,7 +76,6 @@ import java.util.Set;
  * @author Jason D. Rivard
  */
 public class AuthenticationFilter extends AbstractPwmFilter {
-// ------------------------------ FIELDS ------------------------------
 
     private static final PwmLogger LOGGER = PwmLogger.getLogger(AuthenticationFilter.class.getName());
 

+ 0 - 6
server/src/main/java/password/pwm/http/filter/AuthorizationFilter.java

@@ -42,15 +42,9 @@ import java.io.IOException;
  * @author Jason D. Rivard
  */
 public class AuthorizationFilter extends AbstractPwmFilter {
-// ------------------------------ FIELDS ------------------------------
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(AuthenticationFilter.class);
 
-// ------------------------ INTERFACE METHODS ------------------------
-
-
-// --------------------- Interface Filter ---------------------
-
     public void init(final FilterConfig filterConfig)
             throws ServletException {
     }

+ 10 - 10
server/src/main/java/password/pwm/http/filter/ConfigAccessFilter.java

@@ -59,9 +59,9 @@ import password.pwm.util.secure.SecureEngine;
 import javax.servlet.ServletException;
 import java.io.IOException;
 import java.io.Serializable;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.Date;
 import java.util.List;
 
 public class ConfigAccessFilter extends AbstractPwmFilter {
@@ -168,7 +168,7 @@ public class ConfigAccessFilter extends AbstractPwmFilter {
                         final String jsonStr = pwmApplication.getSecureService().decryptStringValue(cookieStr);
                         final PersistentLoginInfo persistentLoginInfo = JsonUtil.deserialize(jsonStr, PersistentLoginInfo.class);
                         if (persistentLoginInfo != null && persistentLoginValue != null) {
-                            if (persistentLoginInfo.getExpireDate().after(new Date())) {
+                            if (persistentLoginInfo.getExpireDate().isAfter(Instant.now())) {
                                 if (persistentLoginValue.equals(persistentLoginInfo.getPassword())) {
                                     persistentLoginAccepted = true;
                                     LOGGER.debug(pwmRequest, "accepting persistent config login from cookie (expires "
@@ -216,7 +216,7 @@ public class ConfigAccessFilter extends AbstractPwmFilter {
             if (persistentLoginEnabled && !persistentLoginAccepted && "on".equals(pwmRequest.readParameterAsString("remember"))) {
                 final int persistentSeconds = figureMaxLoginSeconds(pwmRequest);
                 if (persistentSeconds > 0) {
-                    final Date expirationDate = new Date(System.currentTimeMillis() + (persistentSeconds * 1000));
+                    final Instant expirationDate = Instant.ofEpochMilli(System.currentTimeMillis() + (persistentSeconds * 1000));
                     final PersistentLoginInfo persistentLoginInfo = new PersistentLoginInfo(expirationDate, persistentLoginValue);
                     final String jsonPersistentLoginInfo = JsonUtil.serialize(persistentLoginInfo);
                     final String cookieValue = pwmApplication.getSecureService().encryptToString(jsonPersistentLoginInfo);
@@ -274,7 +274,7 @@ public class ConfigAccessFilter extends AbstractPwmFilter {
         final ConfigLoginHistory configLoginHistory = readConfigLoginHistory(pwmRequest);
         final ConfigLoginEvent event = new ConfigLoginEvent(
                 userIdentity == null ? "n/a" : userIdentity.toDisplayString(),
-                new Date(),
+                Instant.now(),
                 pwmRequest.getPwmSession().getSessionStateBean().getSrcAddress()
         );
         final int maxEvents = Integer.parseInt(pwmRequest.getPwmApplication().getConfig().readAppProperty(AppProperty.CONFIG_HISTORY_MAX_ITEMS));
@@ -283,11 +283,11 @@ public class ConfigAccessFilter extends AbstractPwmFilter {
     }
 
     private static class PersistentLoginInfo implements Serializable {
-        private Date expireDate;
+        private Instant expireDate;
         private String password;
 
         private PersistentLoginInfo(
-                final Date expireDate,
+                final Instant expireDate,
                 final String password
         )
         {
@@ -295,7 +295,7 @@ public class ConfigAccessFilter extends AbstractPwmFilter {
             this.password = password;
         }
 
-        public Date getExpireDate()
+        public Instant getExpireDate()
         {
             return expireDate;
         }
@@ -333,10 +333,10 @@ public class ConfigAccessFilter extends AbstractPwmFilter {
 
     public static class ConfigLoginEvent implements Serializable {
         private final String userIdentity;
-        private final Date date;
+        private final Instant date;
         private final String networkAddress;
 
-        public ConfigLoginEvent(final String userIdentity, final Date date, final String networkAddress) {
+        public ConfigLoginEvent(final String userIdentity, final Instant date, final String networkAddress) {
             this.userIdentity = userIdentity;
             this.date = date;
             this.networkAddress = networkAddress;
@@ -346,7 +346,7 @@ public class ConfigAccessFilter extends AbstractPwmFilter {
             return userIdentity;
         }
 
-        public Date getDate() {
+        public Instant getDate() {
             return date;
         }
 

+ 0 - 2
server/src/main/java/password/pwm/http/filter/GZIPFilter.java

@@ -167,11 +167,9 @@ public class GZIPFilter implements Filter {
 
     public static class ServletResponseGZIPOutputStream extends ServletOutputStream {
         private final AtomicBoolean open = new AtomicBoolean(true);
-        private ServletOutputStream servletOutputStream;
         private GZIPOutputStream gzipStream;
 
         public ServletResponseGZIPOutputStream(final ServletOutputStream output) throws IOException {
-            servletOutputStream = output;
             gzipStream = new GZIPOutputStream(output);
         }
 

+ 30 - 30
server/src/main/java/password/pwm/http/filter/RequestInitializationFilter.java

@@ -155,18 +155,8 @@ public class RequestInitializationFilter implements Filter {
         } catch (Throwable e) {
             LOGGER.error("can't load application: " + e.getMessage(),e);
             if (!(new PwmURL(req).isResourceURL())) {
-                ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_APP_UNAVAILABLE);
-                try {
-                    final ContextManager contextManager = ContextManager.getContextManager(req.getServletContext());
-                    if (contextManager != null) {
-                        errorInformation = contextManager.getStartupErrorInformation();
-                    }
-                } catch (Throwable e2) {
-                    e2.getMessage();
-                }
-                req.setAttribute(PwmRequestAttribute.PwmErrorInfo.toString(),errorInformation);
-                final String url = JspUrl.APP_UNAVAILABLE.getPath();
-                req.getServletContext().getRequestDispatcher(url).forward(req, resp);
+                respondWithUnavalailbleError(req, resp);
+                return;
             }
             return;
         }
@@ -201,18 +191,8 @@ public class RequestInitializationFilter implements Filter {
                 LOGGER.error(logMsg,e);
             }
             if (!(new PwmURL(req).isResourceURL())) {
-                ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_APP_UNAVAILABLE);
-                try {
-                    final ContextManager contextManager = ContextManager.getContextManager(req.getServletContext());
-                    if (contextManager != null) {
-                        errorInformation = contextManager.getStartupErrorInformation();
-                    }
-                } catch (Throwable e2) {
-                    e2.getMessage();
-                }
-                req.setAttribute(PwmRequestAttribute.PwmErrorInfo.toString(),errorInformation);
-                final String url = JspUrl.APP_UNAVAILABLE.getPath();
-                req.getServletContext().getRequestDispatcher(url).forward(req, resp);
+                respondWithUnavalailbleError(req, resp);
+                return;
             }
             return;
         }
@@ -220,6 +200,24 @@ public class RequestInitializationFilter implements Filter {
         filterChain.doFilter(req, resp);
     }
 
+    private void respondWithUnavalailbleError(final HttpServletRequest req, final HttpServletResponse resp)
+            throws ServletException, IOException
+    {
+        ErrorInformation errorInformation = new ErrorInformation(PwmError.ERROR_APP_UNAVAILABLE);
+        try {
+            final ContextManager contextManager = ContextManager.getContextManager(req.getServletContext());
+            if (contextManager != null && contextManager.getStartupErrorInformation() != null) {
+                errorInformation = contextManager.getStartupErrorInformation();
+            }
+        } catch (PwmUnrecoverableException e2) {
+            LOGGER.error("error reading session context from servlet container: " + e2.getMessage());
+        }
+
+        req.setAttribute(PwmRequestAttribute.PwmErrorInfo.toString(),errorInformation);
+        final String url = JspUrl.APP_UNAVAILABLE.getPath();
+        req.getServletContext().getRequestDispatcher(url).forward(req, resp);
+    }
+
     private void checkAndInitSessionState(final HttpServletRequest request)
             throws PwmUnrecoverableException
     {
@@ -284,9 +282,7 @@ public class RequestInitializationFilter implements Filter {
         final HttpSession newSession = req.getSession(true);
 
         // write back all the session data
-        for (final String attrName : sessionAttributes.keySet()) {
-            newSession.setAttribute(attrName, sessionAttributes.get(attrName));
-        }
+        sessionAttributes.keySet().forEach(attrName -> newSession.setAttribute(attrName, sessionAttributes.get(attrName)));
 
         newSession.setMaxInactiveInterval(oldMaxInactiveInterval);
 
@@ -364,7 +360,9 @@ public class RequestInitializationFilter implements Filter {
         }
 
         if (includeXAmb) {
-            resp.setHeader(HttpHeader.XAmb, PwmConstants.X_AMB_HEADER[PwmRandom.getInstance().nextInt(PwmConstants.X_AMB_HEADER.length)]);
+            resp.setHeader(HttpHeader.XAmb, PwmConstants.X_AMB_HEADER.get(
+                    PwmRandom.getInstance().nextInt(PwmConstants.X_AMB_HEADER.size())
+            ));
         }
 
         if (includeContentLanguage) {
@@ -541,9 +539,11 @@ public class RequestInitializationFilter implements Filter {
             final List<String> requiredHeaders = pwmRequest.getConfig().readSettingAsStringArray(PwmSetting.REQUIRED_HEADERS);
             if (requiredHeaders != null && !requiredHeaders.isEmpty()) {
                 final Map<String, String> configuredValues  = StringUtil.convertStringListToNameValuePair(requiredHeaders, "=");
-                for (final String key : configuredValues.keySet()) {
+
+                for (final Map.Entry<String,String> entry : configuredValues.entrySet()) {
+                    final String key = entry.getKey();
                     if (key != null && key.length() > 0) {
-                        final String requiredValue = configuredValues.get(key);
+                        final String requiredValue = entry.getValue();
                         if (requiredValue != null && requiredValue.length() > 0) {
                             final String value = pwmRequest.readHeaderValueAsString(key);
                             if (value == null || value.length() < 1) {

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

@@ -109,7 +109,6 @@ import java.util.concurrent.TimeUnit;
         }
 )
 public class ActivateUserServlet extends AbstractPwmServlet {
-// ------------------------------ FIELDS ------------------------------
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(ActivateUserServlet.class);
 
@@ -479,13 +478,14 @@ public class ActivateUserServlet extends AbstractPwmServlet {
         final String searchFilter = figureLdapSearchFilter(pwmRequest);
         final ChaiUser chaiUser = ChaiFactory.createChaiUser(userIdentity.getUserDN(), pwmApplication.getProxyChaiProvider(userIdentity.getLdapProfileID()));
 
-        for (final FormConfiguration formItem : formValues.keySet()) {
+        for (final Map.Entry<FormConfiguration, String> entry : formValues.entrySet()) {
+            final FormConfiguration formItem = entry.getKey();
             final String attrName = formItem.getName();
             final String tokenizedAttrName = "%" + attrName + "%";
             if (searchFilter.contains(tokenizedAttrName)) {
                 LOGGER.trace(pwmSession, "skipping validation of ldap value for '" + attrName + "' because it is in search filter");
             } else {
-                final String value = formValues.get(formItem);
+                final String value = entry.getValue();
                 try {
                     if (!chaiUser.compareStringAttribute(attrName, value)) {
                         final String errorMsg = "incorrect value for '" + attrName + "'";

+ 13 - 12
server/src/main/java/password/pwm/http/servlet/GuestRegistrationServlet.java

@@ -33,12 +33,11 @@ import password.pwm.PwmConstants;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.LocalSessionStateBean;
 import password.pwm.bean.UserIdentity;
-import password.pwm.config.value.data.ActionConfiguration;
 import password.pwm.config.Configuration;
-import password.pwm.config.value.data.FormConfiguration;
-import password.pwm.util.form.FormUtility;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.profile.PwmPasswordPolicy;
+import password.pwm.config.value.data.ActionConfiguration;
+import password.pwm.config.value.data.FormConfiguration;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
@@ -59,6 +58,7 @@ import password.pwm.svc.stats.Statistic;
 import password.pwm.util.FormMap;
 import password.pwm.util.PasswordData;
 import password.pwm.util.RandomPasswordGenerator;
+import password.pwm.util.form.FormUtility;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroMachine;
@@ -70,6 +70,7 @@ import javax.servlet.annotation.WebServlet;
 import java.io.IOException;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
+import java.time.Instant;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Date;
@@ -302,9 +303,6 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
         final String usernameParam = pwmRequest.readParameterAsString("username");
         final GuestRegistrationBean guBean = pwmApplication.getSessionStateService().getBean(pwmRequest, GuestRegistrationBean.class);
 
-
-        SearchConfiguration.builder();
-
         final SearchConfiguration searchConfiguration = SearchConfiguration.builder()
                     .chaiProvider(chaiProvider)
                     .contexts(Collections.singletonList(config.readSettingAsString(PwmSetting.GUEST_CONTEXT)))
@@ -348,7 +346,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
                 if (expirationAttribute != null && expirationAttribute.length() > 0) {
                     final Date expiration = guestUserInfo.readDateAttribute(expirationAttribute);
                     if (expiration != null) {
-                        guBean.setUpdateUserExpirationDate(expiration);
+                        guBean.setUpdateUserExpirationDate(expiration.toInstant());
                     }
                 }
 
@@ -410,8 +408,10 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
 
             // set up the user creation attributes
             final Map<String,String> createAttributes = new HashMap<>();
-            for (final FormConfiguration formItem : formValues.keySet()) {
-                LOGGER.debug(pwmSession, "Attribute from form: "+ formItem.getName()+" = "+formValues.get(formItem));
+            for (final Map.Entry<FormConfiguration, String> entry : formValues.entrySet()) {
+                final FormConfiguration formItem = entry.getKey();
+                final String value = entry.getValue();
+                LOGGER.debug(pwmSession, "Attribute from form: " + formItem.getName() + " = " + value);
                 final String n = formItem.getName();
                 final String v = formValues.get(formItem);
                 if (n != null && n.length() > 0 && v != null && v.length() > 0) {
@@ -534,9 +534,10 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
     {
         final String namingAttribute = config.getDefaultLdapProfile().readSettingAsString(
                 PwmSetting.LDAP_NAMING_ATTRIBUTE);
-        for (final FormConfiguration formItem : formValues.keySet()) {
+        for (final Map.Entry<FormConfiguration, String> entry : formValues.entrySet()) {
+            final FormConfiguration formItem = entry.getKey();
             if (namingAttribute.equals(formItem.getName())) {
-                final String namingValue = formValues.get(formItem);
+                final String namingValue = entry.getValue();
                 final String gestUserContextDN = config.readSettingAsString(PwmSetting.GUEST_CONTEXT);
                 return namingAttribute + "=" + namingValue + "," + gestUserContextDN;
             }
@@ -645,7 +646,7 @@ public class GuestRegistrationServlet extends AbstractPwmServlet {
         {
             final String selectedDate = guestRegistrationBean.getFormValues().get(HTTP_PARAM_EXPIRATION_DATE);
             if (selectedDate == null || selectedDate.isEmpty()) {
-                final Date currentDate = guestRegistrationBean.getUpdateUserExpirationDate();
+                final Instant currentDate = guestRegistrationBean.getUpdateUserExpirationDate();
 
                 if (currentDate == null) {
                     currentExpirationDate = maxExpirationDate;

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

@@ -58,6 +58,7 @@ import password.pwm.http.servlet.peoplesearch.PublicPeopleSearchServlet;
 
 import javax.servlet.annotation.WebServlet;
 import java.lang.annotation.Annotation;
+import java.util.Arrays;
 
 public enum PwmServletDefinition {
     Login(password.pwm.http.servlet.LoginServlet.class, LoginServletBean.class),
@@ -117,7 +118,7 @@ public enum PwmServletDefinition {
     }
 
     public String[] urlPatterns() {
-        return patterns;
+        return patterns == null ? null : Arrays.copyOf(patterns, patterns.length);
     }
 
     public String servletUrlName() {

+ 5 - 24
server/src/main/java/password/pwm/http/servlet/SetupResponsesServlet.java

@@ -31,11 +31,11 @@ import com.novell.ldapchai.exception.ChaiError;
 import com.novell.ldapchai.exception.ChaiUnavailableException;
 import com.novell.ldapchai.exception.ChaiValidationException;
 import com.novell.ldapchai.provider.ChaiProvider;
+import lombok.Value;
 import password.pwm.Permission;
 import password.pwm.PwmApplication;
 import password.pwm.PwmConstants;
 import password.pwm.bean.ResponseInfoBean;
-import password.pwm.ldap.UserInfo;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.profile.ChallengeProfile;
 import password.pwm.error.ErrorInformation;
@@ -51,6 +51,7 @@ import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.PwmSession;
 import password.pwm.http.bean.SetupResponsesBean;
 import password.pwm.i18n.Message;
+import password.pwm.ldap.UserInfo;
 import password.pwm.ldap.auth.AuthenticationType;
 import password.pwm.svc.event.AuditEvent;
 import password.pwm.svc.event.AuditRecordFactory;
@@ -602,30 +603,10 @@ public class SetupResponsesServlet extends ControlledPwmServlet {
         return setupData;
     }
 
+    @Value
     private static class ValidationResponseBean implements Serializable {
-        private final int version = 1;
-        private final String message;
-        private final boolean success;
-
-        private ValidationResponseBean(
-                final String message,
-                final boolean success
-        ) {
-            this.message = message;
-            this.success = success;
-        }
-
-        public int getVersion() {
-            return version;
-        }
-
-        public String getMessage() {
-            return message;
-        }
-
-        public boolean isSuccess() {
-            return success;
-        }
+        private String message;
+        private boolean success;
     }
 }
 

+ 3 - 2
server/src/main/java/password/pwm/http/servlet/ShortcutServlet.java

@@ -140,9 +140,10 @@ public class ShortcutServlet extends AbstractPwmServlet {
             final Map<String, List<String>> headerValueMap = pwmRequest.readHeaderValuesMap();
             final List<String> interestedHeaderNames = pwmRequest.getConfig().readSettingAsStringArray(PwmSetting.SHORTCUT_HEADER_NAMES);
 
-            for (final String headerName : headerValueMap.keySet()) {
+            for (final Map.Entry<String, List<String>> entry : headerValueMap.entrySet()) {
+                final String headerName = entry.getKey();
                 if (interestedHeaderNames.contains(headerName)) {
-                    for (final String loopValues : headerValueMap.get(headerName)) {
+                    for (final String loopValues : entry.getValue()) {
                         labelsFromHeader.addAll(StringHelper.tokenizeString(loopValues, ","));
                     }
                 }

+ 0 - 1
server/src/main/java/password/pwm/http/servlet/UpdateProfileServlet.java

@@ -594,7 +594,6 @@ public class UpdateProfileServlet extends ControlledPwmServlet {
         final List<FormConfiguration> form = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_FORM);
         final Map<FormConfiguration,String> formValueMap = formMapFromBean(updateAttributesProfile, updateProfileBean);
         pwmRequest.addFormInfoToRequestAttr(form, formValueMap, true, false);
-        final List<FormConfiguration> links = updateAttributesProfile.readSettingAsForm(PwmSetting.UPDATE_PROFILE_CUSTOMLINKS);
         pwmRequest.forwardToJsp(JspUrl.UPDATE_ATTRIBUTES_CONFIRM);
     }
 

+ 1 - 2
server/src/main/java/password/pwm/http/servlet/admin/AppDashboardData.java

@@ -56,7 +56,6 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Date;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
@@ -276,7 +275,7 @@ public class AppDashboardData implements Serializable {
                 numberFormat.format(pwmApplication.getSharedHistoryManager().size())
         ));
         {
-            final Date oldestEntryAge = pwmApplication.getSharedHistoryManager().getOldestEntryTime();
+            final Instant oldestEntryAge = pwmApplication.getSharedHistoryManager().getOldestEntryTime();
             final String display = oldestEntryAge == null
                     ? NA_VALUE
                     : TimeDuration.fromCurrent(oldestEntryAge).asCompactString();

+ 2 - 2
server/src/main/java/password/pwm/http/servlet/admin/ReportStatusBean.java

@@ -24,13 +24,13 @@ package password.pwm.http.servlet.admin;
 
 import password.pwm.svc.report.ReportService;
 import password.pwm.svc.report.ReportStatusInfo;
+import password.pwm.util.java.PwmNumberFormat;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.localdb.LocalDBException;
 
 import java.io.Serializable;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
-import java.text.NumberFormat;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.Locale;
@@ -46,7 +46,7 @@ public class ReportStatusBean implements Serializable {
     public static ReportStatusBean makeReportStatusData(final ReportService reportService, final Locale locale)
             throws LocalDBException
     {
-        final NumberFormat numberFormat = NumberFormat.getInstance();
+        final PwmNumberFormat numberFormat = PwmNumberFormat.forLocale(locale);
 
         final ReportStatusBean returnMap = new ReportStatusBean();
         final ReportStatusInfo reportInfo = reportService.getReportStatusInfo();

+ 0 - 7
server/src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServlet.java

@@ -367,13 +367,6 @@ public abstract class ChangePasswordServlet extends ControlledPwmServlet {
         );
 
         final UserInfo userInfo = pwmRequest.getPwmSession().getUserInfo();
-        final RestCheckPasswordServer.PasswordCheckRequest passwordCheckRequest = new RestCheckPasswordServer.PasswordCheckRequest(
-                userInfo.getUserIdentity(),
-                PasswordData.forStringValue(jsonInput.getPassword1()),
-                PasswordData.forStringValue(jsonInput.getPassword2()),
-                userInfo
-        );
-
         final PasswordUtility.PasswordCheckInfo passwordCheckInfo = PasswordUtility.checkEnteredPassword(
                 pwmRequest.getPwmApplication(),
                 pwmRequest.getLocale(),

+ 3 - 2
server/src/main/java/password/pwm/http/servlet/changepw/ChangePasswordServletUtil.java

@@ -80,9 +80,10 @@ public class ChangePasswordServletUtil {
     )
             throws ChaiUnavailableException, PwmDataValidationException
     {
-        for (final FormConfiguration formItem : formValues.keySet()) {
+        for (final Map.Entry<FormConfiguration, String> entry : formValues.entrySet()) {
+            final FormConfiguration formItem = entry.getKey();
             final String attrName = formItem.getName();
-            final String value = formValues.get(formItem);
+            final String value = entry.getValue();
             try {
                 if (!theUser.compareStringAttribute(attrName, value)) {
                     final String errorMsg = "incorrect value for '" + attrName + "'";

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

@@ -114,7 +114,6 @@ import java.util.concurrent.ConcurrentHashMap;
         }
 )
 public class ConfigEditorServlet extends AbstractPwmServlet {
-// ------------------------------ FIELDS ------------------------------
 
     private static final PwmLogger LOGGER = PwmLogger.forClass(ConfigEditorServlet.class);
 
@@ -624,10 +623,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
                         final String returnCategory = item.getNavigation();
 
 
-                        if (!returnData.containsKey(returnCategory)) {
-                            returnData.put(returnCategory, new ConcurrentHashMap<>());
-                        }
-
+                        returnData.putIfAbsent(returnCategory, new ConcurrentHashMap<>());
                         returnData.get(returnCategory).put(setting.getKey(), item);
                     });
 
@@ -731,7 +727,7 @@ public class ConfigEditorServlet extends AbstractPwmServlet {
                 }
 
                 final Map<String, PwmRequest.FileUploadItem> fileUploads = pwmRequest.readFileUploads(maxFileSize, 1);
-                final ByteArrayInputStream fileIs = new ByteArrayInputStream(fileUploads.get(PwmConstants.PARAM_FILE_UPLOAD).getContent());
+                final ByteArrayInputStream fileIs = new ByteArrayInputStream(fileUploads.get(PwmConstants.PARAM_FILE_UPLOAD).getContent().getBytes());
 
                 HttpsServerCertificateManager.importKey(
                         configManagerBean.getStoredConfiguration(),

+ 2 - 2
server/src/main/java/password/pwm/http/servlet/configguide/ConfigGuideServlet.java

@@ -486,7 +486,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
             } catch (IllegalArgumentException e) { /* */ }
         }
 
-        if (GuideStep.START.equals(requestedStep)) {
+        if (GuideStep.START.equals(GuideStep.valueOf(requestedStep))) {
             configGuideBean.getFormData().clear();
             configGuideBean.getFormData().putAll(ConfigGuideForm.defaultForm());
         }
@@ -587,7 +587,7 @@ public class ConfigGuideServlet extends AbstractPwmServlet {
 
         if (configGuideBean.getStep() == GuideStep.LDAP_PERMISSIONS) {
             final LDAPPermissionCalculator ldapPermissionCalculator = new LDAPPermissionCalculator(ConfigGuideForm.generateStoredConfig(configGuideBean));
-            pwmRequest.setAttribute(PwmRequestAttribute.LdapPermissionItems,ldapPermissionCalculator);
+            pwmRequest.setAttribute(PwmRequestAttribute.LdapPermissionItems, ldapPermissionCalculator);
         }
 
         final HttpServletRequest req = pwmRequest.getHttpServletRequest();

+ 24 - 70
server/src/main/java/password/pwm/http/servlet/configmanager/ConfigManagerCertificatesServlet.java

@@ -23,8 +23,9 @@
 package password.pwm.http.servlet.configmanager;
 
 import com.novell.ldapchai.exception.ChaiUnavailableException;
+import lombok.Builder;
+import lombok.Value;
 import password.pwm.PwmConstants;
-import password.pwm.config.value.data.ActionConfiguration;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSettingSyntax;
@@ -32,14 +33,15 @@ import password.pwm.config.StoredValue;
 import password.pwm.config.stored.StoredConfigReference;
 import password.pwm.config.stored.StoredConfigurationImpl;
 import password.pwm.config.stored.StoredConfigurationUtil;
+import password.pwm.config.value.data.ActionConfiguration;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.HttpMethod;
 import password.pwm.http.JspUrl;
 import password.pwm.http.PwmRequest;
 import password.pwm.http.PwmRequestAttribute;
 import password.pwm.http.servlet.AbstractPwmServlet;
-import password.pwm.util.secure.X509Utils;
 import password.pwm.util.logging.PwmLogger;
+import password.pwm.util.secure.X509Utils;
 import password.pwm.ws.server.RestResultBean;
 
 import javax.servlet.ServletException;
@@ -50,6 +52,7 @@ import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
 import java.time.Instant;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -119,7 +122,8 @@ public class ConfigManagerCertificatesServlet extends AbstractPwmServlet {
                     } else {
                         storedValue = storedConfiguration.readSetting(pwmSetting);
                     }
-                    final X509Certificate[] certificates = (X509Certificate[])storedValue.toNativeObject();
+                    final X509Certificate[] arrayCerts = (X509Certificate[])storedValue.toNativeObject();
+                    final List<X509Certificate> certificates = arrayCerts == null ? Collections.emptyList() : Arrays.asList(arrayCerts);
                     certificateDebugDataItems.addAll(makeItems(pwmSetting, ref.getProfileID(), certificates));
                 } else if (pwmSetting.getSyntax() == PwmSettingSyntax.ACTION) {
                     final StoredValue storedValue;
@@ -130,7 +134,7 @@ public class ConfigManagerCertificatesServlet extends AbstractPwmServlet {
                     }
                     final List<ActionConfiguration> actionConfigurations = (List)storedValue.toNativeObject();
                     for (final ActionConfiguration actionConfiguration : actionConfigurations) {
-                        final X509Certificate[] certificates = actionConfiguration.getCertificates();
+                        final List<X509Certificate> certificates = actionConfiguration.getCertificates();
                         certificateDebugDataItems.addAll(makeItems(pwmSetting, ref.getProfileID(), certificates));
                     }
                 }
@@ -144,7 +148,7 @@ public class ConfigManagerCertificatesServlet extends AbstractPwmServlet {
     Collection<CertificateDebugDataItem> makeItems(
             final PwmSetting setting,
             final String profileId,
-            final X509Certificate[] certificates
+            final List<X509Certificate> certificates
     ) throws PwmUnrecoverableException {
         if (certificates == null) {
             return Collections.emptyList();
@@ -165,21 +169,23 @@ public class ConfigManagerCertificatesServlet extends AbstractPwmServlet {
     )
             throws PwmUnrecoverableException
     {
-        final CertificateDebugDataItem item = new CertificateDebugDataItem();
-        item.setMenuLocation(setting.toMenuLocationDebug(profileId, PwmConstants.DEFAULT_LOCALE));
-        item.setSubject(certificate.getSubjectDN().toString());
-        item.setSerial(certificate.getSerialNumber().toString());
-        item.setAlgorithm(certificate.getSigAlgName());
-        item.setIssueDate(certificate.getNotBefore().toInstant());
-        item.setExpirationDate(certificate.getNotAfter().toInstant());
+        final CertificateDebugDataItem.CertificateDebugDataItemBuilder builder = CertificateDebugDataItem.builder();
+        builder.menuLocation(setting.toMenuLocationDebug(profileId, PwmConstants.DEFAULT_LOCALE));
+        builder.subject(certificate.getSubjectDN().toString());
+        builder.serial(certificate.getSerialNumber().toString());
+        builder.algorithm(certificate.getSigAlgName());
+        builder.issueDate(certificate.getNotBefore().toInstant());
+        builder.expirationDate(certificate.getNotAfter().toInstant());
         try {
-            item.setDetail(X509Utils.makeDetailText(certificate));
+            builder.detail(X509Utils.makeDetailText(certificate));
         } catch (CertificateEncodingException e) {
             LOGGER.error("unexpected error parsing certificate detail text: " + e.getMessage());
         }
-        return item;
+        return builder.build();
     }
 
+    @Value
+    @Builder
     public static class CertificateDebugDataItem implements Serializable, Comparable {
         private String menuLocation;
         private String subject;
@@ -189,64 +195,12 @@ public class ConfigManagerCertificatesServlet extends AbstractPwmServlet {
         private Instant issueDate;
         private String detail;
 
-        public String getMenuLocation() {
-            return menuLocation;
-        }
-
-        public void setMenuLocation(final String menuLocation) {
-            this.menuLocation = menuLocation;
-        }
-
-        public String getSubject() {
-            return subject;
-        }
-
-        public void setSubject(final String subject) {
-            this.subject = subject;
-        }
-
-        public String getSerial() {
-            return serial;
-        }
-
-        public void setSerial(final String serial) {
-            this.serial = serial;
-        }
-
-        public String getAlgorithm() {
-            return algorithm;
-        }
-
-        public void setAlgorithm(final String algorithm) {
-            this.algorithm = algorithm;
-        }
-
-        public Instant getExpirationDate() {
-            return expirationDate;
-        }
-
-        public void setExpirationDate(final Instant expirationDate) {
-            this.expirationDate = expirationDate;
-        }
-
-        public Instant getIssueDate() {
-            return issueDate;
-        }
-
-        public void setIssueDate(final Instant issueDate) {
-            this.issueDate = issueDate;
-        }
-
-        public String getDetail() {
-            return detail;
-        }
-
-        public void setDetail(final String detail) {
-            this.detail = detail;
-        }
-
         @Override
         public int compareTo(final Object o) {
+            if (this == o || this.equals(o)) {
+                return 0;
+            }
+
             return expirationDate.compareTo(((CertificateDebugDataItem)o).getExpirationDate());
         }
     }

+ 13 - 16
server/src/main/java/password/pwm/http/servlet/configmanager/DebugItemGenerator.java

@@ -67,7 +67,6 @@ import java.time.Instant;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
-import java.util.Date;
 import java.util.Enumeration;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -127,11 +126,11 @@ public class DebugItemGenerator {
                 zipOutput.flush();
                 final String finishMsg = "completed output of " + newGeneratorItem.getFilename() + " in " + TimeDuration.fromCurrent(startTime).asCompactString();
                 LOGGER.trace(pwmRequest, finishMsg);
-                debugGeneratorLogFile.printRecord(JavaHelper.toIsoDate(new Date()),finishMsg);
+                debugGeneratorLogFile.printRecord(JavaHelper.toIsoDate(Instant.now()),finishMsg);
             } catch (Throwable e) {
                 final String errorMsg = "unexpected error executing debug item output class '" + serviceClass.getName() + "', error: " + e.toString();
                 LOGGER.error(pwmRequest, errorMsg);
-                debugGeneratorLogFile.printRecord(JavaHelper.toIsoDate(new Date()),errorMsg);
+                debugGeneratorLogFile.printRecord(JavaHelper.toIsoDate(Instant.now()),errorMsg);
                 final Writer stackTraceOutput = new StringWriter();
                 e.printStackTrace(new PrintWriter(stackTraceOutput));
                 debugGeneratorLogFile.printRecord(stackTraceOutput);
@@ -190,8 +189,9 @@ public class DebugItemGenerator {
                     storedConfiguration.getModifiedSettingDebugValues(PwmConstants.DEFAULT_LOCALE, true)
             );
 
-            for (final String key : modifiedSettings.keySet()) {
-                final String value = modifiedSettings.get(key);
+            for (final Map.Entry<String, String> entry : modifiedSettings.entrySet()) {
+                final String key = entry.getKey();
+                final String value = entry.getValue();
                 writer.write(">> Setting > " + key);
                 writer.write("\n");
                 writer.write(value);
@@ -237,11 +237,13 @@ public class DebugItemGenerator {
             };
 
             final Map<PwmAboutProperty,String> infoBean = PwmAboutProperty.makeInfoBean(pwmApplication);
-            for (final PwmAboutProperty aboutProperty : infoBean.keySet()) {
-                outputProps.put(aboutProperty.toString().replace("_","."), infoBean.get(aboutProperty));
+            for (final Map.Entry<PwmAboutProperty, String> entry : infoBean.entrySet()) {
+                final PwmAboutProperty aboutProperty = entry.getKey();
+                final String value = entry.getValue();
+                outputProps.put(aboutProperty.toString().replace("_","."), value);
             }
             final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            outputProps.store(baos, JavaHelper.toIsoDate(new Date()));
+            outputProps.store(baos, JavaHelper.toIsoDate(Instant.now()));
             outputStream.write(baos.toByteArray());
         }
     }
@@ -256,14 +258,9 @@ public class DebugItemGenerator {
         public void outputItem(final PwmApplication pwmApplication, final PwmRequest pwmRequest, final OutputStream outputStream) throws Exception
         {
             final Properties outputProps = JavaHelper.newSortedProperties();
-
-            // java threads
-            final Map<String,String> envProps = System.getenv();
-            for (final String key : envProps.keySet()) {
-                outputProps.put(key, envProps.get(key));
-            }
+            outputProps.putAll(System.getenv());
             final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            outputProps.store(baos,JavaHelper.toIsoDate(new Date()));
+            outputProps.store(baos,JavaHelper.toIsoDate(Instant.now()));
             outputStream.write(baos.toByteArray());
         }
     }
@@ -286,7 +283,7 @@ public class DebugItemGenerator {
             }
 
             final ByteArrayOutputStream baos = new ByteArrayOutputStream();
-            outputProps.store(baos,JavaHelper.toIsoDate(new Date()));
+            outputProps.store(baos,JavaHelper.toIsoDate(Instant.now()));
             outputStream.write(baos.toByteArray());
         }
     }

+ 58 - 41
server/src/main/java/password/pwm/http/servlet/forgottenpw/ForgottenPasswordServlet.java

@@ -100,6 +100,8 @@ import password.pwm.ws.server.RestResultBean;
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -128,8 +130,6 @@ import java.util.Set;
         }
 )
 public class ForgottenPasswordServlet extends ControlledPwmServlet {
-// ------------------------------ FIELDS ------------------------------
-
     private static final PwmLogger LOGGER = PwmLogger.forClass(ForgottenPasswordServlet.class);
 
     public enum ForgottenPasswordAction implements AbstractPwmServlet.ProcessAction {
@@ -492,10 +492,11 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
         final Map<String,String> remoteResponses = new LinkedHashMap<>();
         {
             final Map<String,String> inputMap = pwmRequest.readParametersAsMap();
-            for (final String name : inputMap.keySet()) {
+            for (final Map.Entry<String, String> entry : inputMap.entrySet()) {
+                final String name = entry.getKey();
                 if (name != null && name.startsWith(PREFIX)) {
                     final String strippedName = name.substring(PREFIX.length(), name.length());
-                    final String value = inputMap.get(name);
+                    final String value = entry.getValue();
                     remoteResponses.put(strippedName, value);
                 }
             }
@@ -747,11 +748,13 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
 
             final Map<FormConfiguration,String> formValues = FormUtility.readFormValuesFromRequest(
                     pwmRequest, requiredAttributesForm, userLocale);
-            for (final FormConfiguration paramConfig : formValues.keySet()) {
+
+            for (final Map.Entry<FormConfiguration, String> entry : formValues.entrySet()) {
+                final FormConfiguration paramConfig = entry.getKey();
                 final String attrName = paramConfig.getName();
 
                 try {
-                    if (theUser.compareStringAttribute(attrName, formValues.get(paramConfig))) {
+                    if (theUser.compareStringAttribute(attrName, entry.getValue())) {
                         LOGGER.trace(pwmRequest, "successful validation of ldap attribute value for '" + attrName + "'");
                     } else {
                         throw new PwmDataValidationException(new ErrorInformation(PwmError.ERROR_INCORRECT_RESPONSE, "incorrect value for '" + attrName + "'", new String[]{attrName}));
@@ -1135,44 +1138,58 @@ public class ForgottenPasswordServlet extends ControlledPwmServlet {
             final UserIdentity userIdentity
     )
     {
-        final PostChangePasswordAction postAction = new PostChangePasswordAction() {
-            @Override
-            public String getLabel() {
-                return "Forgotten Password Post Actions";
-            }
+        final PostChangePasswordAction postAction = new PostChangeAction(pwmRequest.getPwmApplication(), userIdentity);
+        pwmRequest.getPwmSession().getUserSessionDataCacheBean().addPostChangePasswordActions("forgottenPasswordPostActions", postAction);
+    }
 
-            @Override
-            public boolean doAction(final PwmSession pwmSession, final String newPassword)
-                    throws PwmUnrecoverableException {
-                try {
-                    {  // execute configured actions
-                        final ChaiUser proxiedUser = pwmRequest.getPwmApplication().getProxiedChaiUser(userIdentity);
-                        LOGGER.debug(pwmSession, "executing post-forgotten password configured actions to user " + proxiedUser.getEntryDN());
-                        final List<ActionConfiguration> configValues = pwmRequest.getConfig().readSettingAsAction(PwmSetting.FORGOTTEN_USER_POST_ACTIONS);
-                        final ActionExecutor actionExecutor = new ActionExecutor.ActionExecutorSettings(pwmRequest.getPwmApplication(),userIdentity)
-                                .setMacroMachine(pwmSession.getSessionManager().getMacroMachine(pwmRequest.getPwmApplication()))
-                                .setExpandPwmMacros(true)
-                                .createActionExecutor();
-
-                        actionExecutor.executeActions(configValues, pwmSession.getLabel());
-                    }
-                } catch (PwmOperationalException e) {
-                    final ErrorInformation info = new ErrorInformation(PwmError.ERROR_UNKNOWN, e.getErrorInformation().getDetailedErrorMsg(), e.getErrorInformation().getFieldValues());
-                    final PwmUnrecoverableException newException = new PwmUnrecoverableException(info);
-                    newException.initCause(e);
-                    throw newException;
-                } catch (ChaiUnavailableException e) {
-                    final String errorMsg = "unable to reach ldap server while writing post-forgotten password attributes: " + e.getMessage();
-                    final ErrorInformation info = new ErrorInformation(PwmError.ERROR_ACTIVATION_FAILURE, errorMsg);
-                    final PwmUnrecoverableException newException = new PwmUnrecoverableException(info);
-                    newException.initCause(e);
-                    throw newException;
+    private static class PostChangeAction implements PostChangePasswordAction, Serializable {
+
+        private final transient PwmApplication pwmApplication;
+        private final transient UserIdentity userIdentity;
+
+        PostChangeAction(final PwmApplication pwmApplication, final UserIdentity userIdentity) {
+            this.pwmApplication = pwmApplication;
+            this.userIdentity = userIdentity;
+        }
+
+        private void readObject(final ObjectInputStream in) throws IOException,ClassNotFoundException {
+            throw new IllegalStateException("this class does not support deserialization");
+        }
+
+        @Override
+        public String getLabel() {
+            return "Forgotten Password Post Actions";
+        }
+
+        @Override
+        public boolean doAction(final PwmSession pwmSession, final String newPassword)
+                throws PwmUnrecoverableException {
+            try {
+                {  // execute configured actions
+                    final ChaiUser proxiedUser = pwmApplication.getProxiedChaiUser(userIdentity);
+                    LOGGER.debug(pwmSession, "executing post-forgotten password configured actions to user " + proxiedUser.getEntryDN());
+                    final List<ActionConfiguration> configValues = pwmApplication.getConfig().readSettingAsAction(PwmSetting.FORGOTTEN_USER_POST_ACTIONS);
+                    final ActionExecutor actionExecutor = new ActionExecutor.ActionExecutorSettings(pwmApplication, userIdentity)
+                            .setMacroMachine(pwmSession.getSessionManager().getMacroMachine(pwmApplication))
+                            .setExpandPwmMacros(true)
+                            .createActionExecutor();
+
+                    actionExecutor.executeActions(configValues, pwmSession.getLabel());
                 }
-                return true;
+            } catch (PwmOperationalException e) {
+                final ErrorInformation info = new ErrorInformation(PwmError.ERROR_UNKNOWN, e.getErrorInformation().getDetailedErrorMsg(), e.getErrorInformation().getFieldValues());
+                final PwmUnrecoverableException newException = new PwmUnrecoverableException(info);
+                newException.initCause(e);
+                throw newException;
+            } catch (ChaiUnavailableException e) {
+                final String errorMsg = "unable to reach ldap server while writing post-forgotten password attributes: " + e.getMessage();
+                final ErrorInformation info = new ErrorInformation(PwmError.ERROR_ACTIVATION_FAILURE, errorMsg);
+                final PwmUnrecoverableException newException = new PwmUnrecoverableException(info);
+                newException.initCause(e);
+                throw newException;
             }
-        };
-
-        pwmRequest.getPwmSession().getUserSessionDataCacheBean().addPostChangePasswordActions("forgottenPasswordPostActions", postAction);
+            return true;
+        }
     }
 
 

+ 3 - 2
server/src/main/java/password/pwm/http/servlet/newuser/NewUserForm.java

@@ -58,8 +58,9 @@ public class NewUserForm implements Serializable {
             return false;
         }
 
-        for (final String formKey : formData.keySet()) {
-            final String value = formData.get(formKey);
+        for (final Map.Entry<String,String> entry : formData.entrySet()) {
+            final String formKey = entry.getKey();
+            final String value = entry.getValue();
             final String otherValue = otherForm.formData.get(formKey);
             if (value != null && !value.equals(otherValue)) {
                 return false;

+ 6 - 3
server/src/main/java/password/pwm/http/servlet/oauth/OAuthMachine.java

@@ -243,15 +243,18 @@ public class OAuthMachine {
         bodyEntity.setContentType(HttpContentType.form.getHeaderValue());
         httpPost.setEntity(bodyEntity);
 
-        final X509Certificate[] certs = settings.getCertificates();
+        final List<X509Certificate> certs = settings.getCertificates();
 
         final HttpResponse httpResponse;
         final String bodyResponse;
         try {
-            if (certs == null || certs.length == 0) {
+            if (JavaHelper.isEmpty(certs)) {
                 httpResponse = PwmHttpClient.getHttpClient(pwmRequest.getConfig()).execute(httpPost);
             } else {
-                httpResponse = PwmHttpClient.getHttpClient(pwmRequest.getConfig(), new PwmHttpClientConfiguration.Builder().setCertificate(certs).create()).execute(httpPost);
+                httpResponse = PwmHttpClient.getHttpClient(pwmRequest.getConfig(), PwmHttpClientConfiguration.builder()
+                        .certificates(certs)
+                        .build()
+                ).execute(httpPost);
             }
             bodyResponse = EntityUtils.toString(httpResponse.getEntity());
         } catch (PwmException | IOException e) {

+ 3 - 2
server/src/main/java/password/pwm/http/servlet/oauth/OAuthSettings.java

@@ -29,6 +29,7 @@ import password.pwm.util.PasswordData;
 
 import java.io.Serializable;
 import java.security.cert.X509Certificate;
+import java.util.List;
 
 public class OAuthSettings implements Serializable {
     private String loginURL;
@@ -38,7 +39,7 @@ public class OAuthSettings implements Serializable {
     private PasswordData secret;
     private String dnAttributeName;
     private OAuthUseCase use;
-    private X509Certificate[] certificates;
+    private List<X509Certificate> certificates;
     private String usernameSendValue;
 
 
@@ -80,7 +81,7 @@ public class OAuthSettings implements Serializable {
         return use;
     }
 
-    public X509Certificate[] getCertificates() {
+    public List<X509Certificate> getCertificates() {
         return certificates;
     }
 

+ 3 - 2
server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchDataReader.java

@@ -248,8 +248,9 @@ class PeopleSearchDataReader {
         }
         final List<LinkReferenceBean> returnList = new ArrayList<>();
         final MacroMachine macroMachine = getMacroMachine(actorIdentity);
-        for (final String key : linkMap.keySet()) {
-            final String value = linkMap.get(key);
+        for (final Map.Entry<String,String> entry : linkMap.entrySet()) {
+            final String key = entry.getKey();
+            final String value = entry.getValue();
             final String parsedValue = macroMachine.expandMacros(value);
             final LinkReferenceBean linkReference = new LinkReferenceBean();
             linkReference.setName(key);

+ 3 - 6
server/src/main/java/password/pwm/http/servlet/peoplesearch/PeopleSearchResourcesServlet.java

@@ -22,17 +22,15 @@
 
 package password.pwm.http.servlet.peoplesearch;
 
-import java.io.IOException;
+import org.apache.commons.lang3.StringUtils;
+import password.pwm.PwmConstants;
 
 import javax.servlet.ServletException;
 import javax.servlet.annotation.WebServlet;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
-
-import org.apache.commons.lang3.StringUtils;
-
-import password.pwm.PwmConstants;
+import java.io.IOException;
 
 @WebServlet(
     name="PeopleSearchResourcesServlet",
@@ -42,7 +40,6 @@ import password.pwm.PwmConstants;
     }
 )
 public class PeopleSearchResourcesServlet extends HttpServlet {
-    private static final long serialVersionUID = 1L;
 
     @Override
     protected void service(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException {

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

@@ -22,20 +22,22 @@
 
 package password.pwm.http.servlet.resource;
 
+import password.pwm.http.bean.ImmutableByteArray;
+
 import java.io.Serializable;
 import java.util.Map;
 
 final class CacheEntry implements Serializable {
-    private final byte[] entity;
+    private final ImmutableByteArray entity;
     private final Map<String, String> headerStrings;
 
     CacheEntry(final byte[] entity, final Map<String, String> headerStrings) {
-        this.entity = entity;
+        this.entity = new ImmutableByteArray(entity);
         this.headerStrings = headerStrings;
     }
 
     public byte[] getEntity() {
-        return entity;
+        return entity.getBytes();
     }
 
     public Map<String, String> getHeaderStrings() {

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

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

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

@@ -421,10 +421,11 @@ public class ResourceFileServlet extends HttpServlet implements PwmServlet {
 
         {// check files system zip files.
             final Map<String,ZipFile> zipResources = resourceServletConfiguration.getZipResources();
-            for (final String path : zipResources.keySet()) {
+            for (final Map.Entry<String,ZipFile> entry : zipResources.entrySet()) {
+                final String path = entry.getKey();
                 if (filename.startsWith(path)) {
                     final String zipSubPath = filename.substring(path.length() + 1, filename.length());
-                    final ZipFile zipFile = zipResources.get(path);
+                    final ZipFile zipFile = entry.getValue();
                     final ZipEntry zipEntry = zipFile.getEntry(zipSubPath);
                     if (zipEntry != null) {
                         return new ZipFileResource(zipFile, zipEntry);
@@ -466,10 +467,11 @@ public class ResourceFileServlet extends HttpServlet implements PwmServlet {
 
         if (!fileSystemResource.exists()) { // check custom (configuration defined) zip file bundles
             final Map<String,FileResource> customResources = resourceServletConfiguration.getCustomFileBundle();
-            for (final String customFileName : customResources.keySet()) {
+            for (final Map.Entry<String,FileResource> entry : customResources.entrySet()) {
+                final String customFileName = entry.getKey();
                 final String testName = RESOURCE_PATH + "/" + customFileName;
                 if (testName.equals(resourcePathUri)) {
-                    return customResources.get(customFileName);
+                    return entry.getValue();
                 }
             }
         }
@@ -513,7 +515,7 @@ public class ResourceFileServlet extends HttpServlet implements PwmServlet {
             addExpirationHeaders(resourceServletConfiguration, response);
             if (bodyText != null && bodyText.length() > 0) {
                 response.setIntHeader("Content-Length", bodyText.length());
-                copy(new ByteArrayInputStream(bodyText.getBytes()), response.getOutputStream());
+                copy(new ByteArrayInputStream(bodyText.getBytes(PwmConstants.DEFAULT_CHARSET)), response.getOutputStream());
             } else {
                 response.setIntHeader("Content-Length", 0);
             }

+ 7 - 5
server/src/main/java/password/pwm/http/servlet/resource/ResourceServletConfiguration.java

@@ -29,6 +29,7 @@ import password.pwm.PwmApplication;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.value.FileValue;
+import password.pwm.http.bean.ImmutableByteArray;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.logging.PwmLogger;
 
@@ -104,8 +105,9 @@ class ResourceServletConfiguration {
 
         final Map<FileValue.FileInformation, FileValue.FileContent> files = configuration.readSettingAsFile(PwmSetting.DISPLAY_CUSTOM_RESOURCE_BUNDLE);
         if (files != null && !files.isEmpty()) {
-            final FileValue.FileInformation fileInformation = files.keySet().iterator().next();
-            final FileValue.FileContent fileContent = files.get(fileInformation);
+            final Map.Entry<FileValue.FileInformation, FileValue.FileContent> entry = files.entrySet().iterator().next();
+            final FileValue.FileInformation fileInformation = entry.getKey();
+            final FileValue.FileContent fileContent = entry.getValue();
             LOGGER.debug("examining configured zip file resource for items name=" + fileInformation.getFilename() + ", size=" + fileContent.size());
 
             try {
@@ -164,10 +166,10 @@ class ResourceServletConfiguration {
         return maxCacheItems;
     }
 
-    private static Map<String, FileResource> makeMemoryFileMapFromZipInput(final byte[] content)
+    private static Map<String, FileResource> makeMemoryFileMapFromZipInput(final ImmutableByteArray content)
             throws IOException
     {
-        final ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(content));
+        final ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(content.getBytes()));
         final Map<String, FileResource> memoryMap = new HashMap<>();
 
         ZipEntry entry;
@@ -177,7 +179,7 @@ class ResourceServletConfiguration {
                 final long lastModified = entry.getTime();
                 final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                 IOUtils.copy(stream,byteArrayOutputStream);
-                final byte[] contents = byteArrayOutputStream.toByteArray();
+                final ImmutableByteArray contents = new ImmutableByteArray(byteArrayOutputStream.toByteArray());
                 memoryMap.put(name,new MemoryFileResource(name,contents,lastModified));
                 LOGGER.trace("discovered file in configured resource bundle: " + entry.getName());
             }

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

@@ -84,8 +84,7 @@ public class ResourceServletService implements PwmService {
     public long bytesInCache() {
         final Map<CacheKey, CacheEntry> cacheCopy = new HashMap<>(cache.asMap());
         long cacheByteCount = 0;
-        for (final CacheKey cacheKey : cacheCopy.keySet()) {
-            final CacheEntry cacheEntry = cacheCopy.get(cacheKey);
+        for (final CacheEntry cacheEntry : cacheCopy.values()) {
             if (cacheEntry != null && cacheEntry.getEntity() != null) {
                 cacheByteCount += cacheEntry.getEntity().length;
             }

+ 4 - 3
server/src/main/java/password/pwm/http/state/CryptoCookieBeanImpl.java

@@ -117,14 +117,15 @@ class CryptoCookieBeanImpl implements SessionBeanProvider {
             if (pwmRequest != null && pwmRequest.getPwmResponse() != null) {
                 final Map<Class<? extends PwmSessionBean>,PwmSessionBean> beansInRequest = getRequestBeanMap(pwmRequest);
                 if (beansInRequest != null) {
-                    for (final Class<? extends PwmSessionBean> theClass : beansInRequest.keySet()) {
+                    for (final Map.Entry<Class<? extends PwmSessionBean>,PwmSessionBean> entry : beansInRequest.entrySet()) {
+                        final Class<? extends PwmSessionBean> theClass = entry.getKey();
                         final String cookieName = nameForClass(theClass);
-                        final PwmSessionBean bean = beansInRequest.get(theClass);
+                        final PwmSessionBean bean = entry.getValue();
                         if (bean == null) {
                             pwmRequest.getPwmResponse().removeCookie(cookieName, COOKIE_PATH);
                         } else {
                             final PwmSecurityKey key = keyForSession(pwmRequest);
-                            final String encrytedValue = pwmRequest.getPwmApplication().getSecureService().encryptObjectToString(beansInRequest.get(theClass), key);
+                            final String encrytedValue = pwmRequest.getPwmApplication().getSecureService().encryptObjectToString(entry.getValue(), key);
                             pwmRequest.getPwmResponse().writeCookie(cookieName, encrytedValue, -1, COOKIE_PATH);
                         }
                     }

+ 0 - 1
server/src/main/java/password/pwm/http/state/CryptoRequestBeanImpl.java

@@ -74,7 +74,6 @@ public class CryptoRequestBeanImpl implements SessionBeanProvider {
         if (cachedMap.size() > 1) {
             throw new IllegalStateException("unable to handle multiple session state beans");
         }
-        final Class beanClass= cachedMap.keySet().iterator().next();
         final PwmSessionBean bean = cachedMap.values().iterator().next();
         return secureService.encryptObjectToString(bean);
     }

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio