浏览代码

config key comparator improvements and tests

Jason Rivard 4 年之前
父节点
当前提交
64f4ce733c

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

@@ -21,6 +21,7 @@
 package password.pwm.config;
 
 import lombok.Value;
+import password.pwm.PwmConstants;
 import password.pwm.config.value.PasswordValue;
 import password.pwm.config.value.StoredValue;
 import password.pwm.config.value.ValueFactory;
@@ -37,19 +38,20 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.EnumMap;
 import java.util.EnumSet;
 import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.function.Supplier;
 import java.util.regex.Pattern;
 import java.util.regex.PatternSyntaxException;
+import java.util.stream.Collectors;
 
 
 /**
@@ -1297,6 +1299,9 @@ public enum PwmSetting
 
     private static final PwmLogger LOGGER = PwmLogger.forClass( PwmSetting.class );
 
+    private static final Map<String, PwmSetting> KEY_MAP = Collections.unmodifiableMap( Arrays.stream( values() )
+            .collect( Collectors.toMap( PwmSetting::getKey, pwmSetting -> pwmSetting ) ) );
+
     private final String key;
     private final PwmSettingSyntax syntax;
     private final PwmSettingCategory category;
@@ -1312,7 +1317,9 @@ public enum PwmSetting
     private final Supplier<Boolean> hidden = new LazySupplier<>( () -> PwmSettingReader.readHidden( PwmSetting.this ) );
     private final Supplier<Integer> level = new LazySupplier<>( () -> PwmSettingReader.readLevel( PwmSetting.this ) );
     private final Supplier<Pattern> pattern = new LazySupplier<>( () -> PwmSettingReader.readPattern( PwmSetting.this ) );
-    private final Supplier<Pattern> defaultLocaleLabel = new LazySupplier<>( () -> PwmSettingReader.readPattern( PwmSetting.this ) );
+    private final Supplier<String> defaultLocaleLabel = new LazySupplier<>( () -> PwmSettingReader.readLabel( this, PwmConstants.DEFAULT_LOCALE ) );
+    private final Supplier<String> defaultMenuLocation = new LazySupplier<>( () -> PwmSettingReader.readMenuLocationDebugDefault( this ) );
+
 
     PwmSetting(
             final String key,
@@ -1387,8 +1394,12 @@ public enum PwmSetting
 
     public String getLabel( final Locale locale )
     {
-        final String propertyKey = password.pwm.i18n.PwmSetting.SETTING_LABEL_PREFIX + this.getKey();
-        return LocaleHelper.getLocalizedMessage( locale, propertyKey, null, password.pwm.i18n.PwmSetting.class );
+        if ( PwmConstants.DEFAULT_LOCALE.equals( locale ) )
+        {
+            return defaultLocaleLabel.get();
+        }
+
+        return PwmSettingReader.readLabel( this, locale );
     }
 
     public String getDescription( final Locale locale )
@@ -1426,9 +1437,7 @@ public enum PwmSetting
 
     public static Optional<PwmSetting> forKey( final String key )
     {
-        return Arrays.stream( values() )
-                .filter( loopValue -> loopValue.getKey().equals( key ) )
-                .findFirst();
+        return Optional.ofNullable( KEY_MAP.get( key ) );
     }
 
     public String toMenuLocationDebug(
@@ -1436,8 +1445,16 @@ public enum PwmSetting
             final Locale locale
     )
     {
-        final String separator = LocaleHelper.getLocalizedMessage( locale, Config.Display_SettingNavigationSeparator, null );
-        return this.getCategory().toMenuLocationDebug( profileID, locale ) + separator + this.getLabel( locale );
+        final String output;
+        if ( PwmConstants.DEFAULT_LOCALE.equals( locale ) && StringUtil.isEmpty( profileID ) )
+        {
+            output = defaultMenuLocation.get();
+        }
+        else
+        {
+            output = PwmSettingReader.readMenuLocationDebug( this, profileID, locale );
+        }
+        return output;
     }
 
     public Collection<LDAPPermissionInfo> getLDAPPermissionInfo()
@@ -1445,12 +1462,12 @@ public enum PwmSetting
         return ldapPermissionInfo.get();
     }
 
-    public enum SettingStat
-    {
-        Total,
-        hasProfile,
-        syntaxCounts,
-    }
+public enum SettingStat
+{
+    Total,
+    hasProfile,
+    syntaxCounts,
+}
 
     public static Map<SettingStat, Object> getStats( )
     {
@@ -1484,253 +1501,272 @@ public enum PwmSetting
         return returnObj;
     }
 
-    @Value
-    static class TemplateSetReference<T>
-    {
-        private final T reference;
-        private final Set<PwmSettingTemplate> settingTemplates;
+@Value
+static class TemplateSetReference<T>
+{
+    private final T reference;
+    private final Set<PwmSettingTemplate> settingTemplates;
 
-        private static <T> T referenceForTempleSet(
-                final List<TemplateSetReference<T>> templateSetReferences,
-                final PwmSettingTemplateSet pwmSettingTemplate
-        )
+    private static <T> T referenceForTempleSet(
+            final List<TemplateSetReference<T>> templateSetReferences,
+            final PwmSettingTemplateSet pwmSettingTemplate
+    )
+    {
+        if ( templateSetReferences == null || templateSetReferences.isEmpty() )
         {
-            if ( templateSetReferences == null || templateSetReferences.isEmpty() )
-            {
-                throw new IllegalStateException( "templateSetReferences can not be null" );
-            }
+            throw new IllegalStateException( "templateSetReferences can not be null" );
+        }
 
-            if ( templateSetReferences.size() == 1 )
-            {
-                return templateSetReferences.iterator().next().getReference();
-            }
+        if ( templateSetReferences.size() == 1 )
+        {
+            return templateSetReferences.iterator().next().getReference();
+        }
 
-            for ( int matchCountExamSize = templateSetReferences.size(); matchCountExamSize > 0; matchCountExamSize-- )
+        for ( int matchCountExamSize = templateSetReferences.size(); matchCountExamSize > 0; matchCountExamSize-- )
+        {
+            for ( final TemplateSetReference<T> templateSetReference : templateSetReferences )
             {
-                for ( final TemplateSetReference<T> templateSetReference : templateSetReferences )
+                final Set<PwmSettingTemplate> temporarySet = JavaHelper.copiedEnumSet( templateSetReference.getSettingTemplates(), PwmSettingTemplate.class );
+                temporarySet.retainAll( pwmSettingTemplate.getTemplates() );
+                final int matchCount = temporarySet.size();
+                if ( matchCount == matchCountExamSize )
                 {
-                    final Set<PwmSettingTemplate> temporarySet = JavaHelper.copiedEnumSet( templateSetReference.getSettingTemplates(), PwmSettingTemplate.class );
-                    temporarySet.retainAll( pwmSettingTemplate.getTemplates() );
-                    final int matchCount = temporarySet.size();
-                    if ( matchCount == matchCountExamSize )
-                    {
-                        return templateSetReference.getReference();
-                    }
+                    return templateSetReference.getReference();
                 }
             }
-
-            return templateSetReferences.iterator().next().getReference();
         }
+
+        return templateSetReferences.iterator().next().getReference();
     }
+}
 
     public static Set<PwmSetting> sortedByMenuLocation( final Locale locale )
     {
-        final TreeMap<String, PwmSetting> treeMap = new TreeMap<>();
-        for ( final PwmSetting pwmSetting : PwmSetting.values() )
-        {
-            treeMap.put( pwmSetting.toMenuLocationDebug( null, locale ), pwmSetting );
-        }
-        return Collections.unmodifiableSet( new LinkedHashSet<>( treeMap.values() ) );
+        final Set<PwmSetting> sortedSet = new TreeSet<>( menuLocationComparator( locale ) );
+        sortedSet.addAll( KEY_MAP.values() );
+        return Collections.unmodifiableSet( sortedSet );
     }
 
-    static class PwmSettingReader
+    public static Comparator<PwmSetting> menuLocationComparator( final Locale locale )
     {
-
-        private static Collection<PwmSettingFlag> readFlags( final PwmSetting pwmSetting )
+        return ( o1, o2 ) ->
         {
-            final Collection<PwmSettingFlag> returnObj = EnumSet.noneOf( PwmSettingFlag.class );
-            final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
-            final List<XmlElement> flagElements = settingElement.getChildren( "flag" );
-            for ( final XmlElement flagElement : flagElements )
-            {
-                final String value = flagElement.getTextTrim();
+            final String selfValue = o1.toMenuLocationDebug( null, locale );
+            final String otherValue = o2.toMenuLocationDebug( null, locale );
+            return selfValue.compareTo( otherValue );
+        };
+    }
 
-                try
-                {
-                    final PwmSettingFlag flag = PwmSettingFlag.valueOf( value );
-                    returnObj.add( flag );
-                }
-                catch ( final IllegalArgumentException e )
-                {
-                    LOGGER.error( () -> "unknown flag for setting " + pwmSetting.getKey() + ", error: unknown flag value: " + value );
-                }
+static class PwmSettingReader
+{
+    private static Collection<PwmSettingFlag> readFlags( final PwmSetting pwmSetting )
+    {
+        final Collection<PwmSettingFlag> returnObj = EnumSet.noneOf( PwmSettingFlag.class );
+        final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
+        final List<XmlElement> flagElements = settingElement.getChildren( "flag" );
+        for ( final XmlElement flagElement : flagElements )
+        {
+            final String value = flagElement.getTextTrim();
 
+            try
+            {
+                final PwmSettingFlag flag = PwmSettingFlag.valueOf( value );
+                returnObj.add( flag );
             }
-            return Collections.unmodifiableCollection( returnObj );
+            catch ( final IllegalArgumentException e )
+            {
+                LOGGER.error( () -> "unknown flag for setting " + pwmSetting.getKey() + ", error: unknown flag value: " + value );
+            }
+
         }
+        return Collections.unmodifiableCollection( returnObj );
+    }
 
-        private static Map<String, String> readOptions( final PwmSetting pwmSetting )
+    private static Map<String, String> readOptions( final PwmSetting pwmSetting )
+    {
+        final Map<String, String> returnList = new LinkedHashMap<>();
+        final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
+        final Optional<XmlElement> optionsElement = settingElement.getChild( PwmSettingXml.XML_ELEMENT_OPTIONS );
+        if ( optionsElement.isPresent() )
         {
-            final Map<String, String> returnList = new LinkedHashMap<>();
-            final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
-            final Optional<XmlElement> optionsElement = settingElement.getChild( PwmSettingXml.XML_ELEMENT_OPTIONS );
-            if ( optionsElement.isPresent() )
+            final List<XmlElement> optionElements = optionsElement.get().getChildren( PwmSettingXml.XML_ELEMENT_OPTION );
+            if ( optionElements != null )
             {
-                final List<XmlElement> optionElements = optionsElement.get().getChildren( PwmSettingXml.XML_ELEMENT_OPTION );
-                if ( optionElements != null )
+                for ( final XmlElement optionElement : optionElements )
                 {
-                    for ( final XmlElement optionElement : optionElements )
+                    if ( optionElement.getAttributeValue( PwmSettingXml.XML_ELEMENT_VALUE ) == null )
                     {
-                        if ( optionElement.getAttributeValue( PwmSettingXml.XML_ELEMENT_VALUE ) == null )
-                        {
-                            throw new IllegalStateException( "option element is missing 'value' attribute for key " + pwmSetting.getKey() );
-                        }
-                        returnList.put( optionElement.getAttributeValue( PwmSettingXml.XML_ELEMENT_VALUE ), optionElement.getText() );
+                        throw new IllegalStateException( "option element is missing 'value' attribute for key " + pwmSetting.getKey() );
                     }
+                    returnList.put( optionElement.getAttributeValue( PwmSettingXml.XML_ELEMENT_VALUE ), optionElement.getText() );
                 }
             }
-            final Map<String, String> finalList = Collections.unmodifiableMap( returnList );
-            return Collections.unmodifiableMap( finalList );
         }
+        final Map<String, String> finalList = Collections.unmodifiableMap( returnList );
+        return Collections.unmodifiableMap( finalList );
+    }
 
-        private static Collection<LDAPPermissionInfo> readLdapPermissionInfo( final PwmSetting pwmSetting )
+    private static Collection<LDAPPermissionInfo> readLdapPermissionInfo( final PwmSetting pwmSetting )
+    {
+        final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
+        final List<XmlElement> permissionElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_LDAP_PERMISSION );
+        final List<LDAPPermissionInfo> returnObj = new ArrayList<>();
+        if ( permissionElements != null )
         {
-            final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
-            final List<XmlElement> permissionElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_LDAP_PERMISSION );
-            final List<LDAPPermissionInfo> returnObj = new ArrayList<>();
-            if ( permissionElements != null )
+            for ( final XmlElement permissionElement : permissionElements )
             {
-                for ( final XmlElement permissionElement : permissionElements )
+                final Optional<LDAPPermissionInfo.Actor> actor = JavaHelper.readEnumFromString(
+                        LDAPPermissionInfo.Actor.class,
+                        permissionElement.getAttributeValue( PwmSettingXml.XML_ATTRIBUTE_PERMISSION_ACTOR )
+                );
+                final Optional<LDAPPermissionInfo.Access> type = JavaHelper.readEnumFromString(
+                        LDAPPermissionInfo.Access.class,
+                        permissionElement.getAttributeValue( PwmSettingXml.XML_ATTRIBUTE_PERMISSION_ACCESS )
+                );
+                if ( actor.isPresent() && type.isPresent() )
                 {
-                    final Optional<LDAPPermissionInfo.Actor> actor = JavaHelper.readEnumFromString(
-                            LDAPPermissionInfo.Actor.class,
-                            permissionElement.getAttributeValue( PwmSettingXml.XML_ATTRIBUTE_PERMISSION_ACTOR )
-                    );
-                    final Optional<LDAPPermissionInfo.Access> type = JavaHelper.readEnumFromString(
-                            LDAPPermissionInfo.Access.class,
-                            permissionElement.getAttributeValue( PwmSettingXml.XML_ATTRIBUTE_PERMISSION_ACCESS )
-                    );
-                    if ( actor.isPresent() && type.isPresent() )
-                    {
-                        final LDAPPermissionInfo permissionInfo = new LDAPPermissionInfo( type.get(), actor.get() );
-                        returnObj.add( permissionInfo );
-                    }
+                    final LDAPPermissionInfo permissionInfo = new LDAPPermissionInfo( type.get(), actor.get() );
+                    returnObj.add( permissionInfo );
                 }
             }
-            return Collections.unmodifiableList( returnObj );
         }
+        return Collections.unmodifiableList( returnObj );
+    }
 
-        private static List<TemplateSetReference<String>> readExamples( final PwmSetting pwmSetting )
+    private static List<TemplateSetReference<String>> readExamples( final PwmSetting pwmSetting )
+    {
+        final List<TemplateSetReference<String>> returnObj = new ArrayList<>();
+        final MacroMachine macroMachine = MacroMachine.forStatic();
+        final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
+        final List<XmlElement> exampleElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_EXAMPLE );
+        for ( final XmlElement exampleElement : exampleElements )
         {
-            final List<TemplateSetReference<String>> returnObj = new ArrayList<>();
-            final MacroMachine macroMachine = MacroMachine.forStatic();
-            final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
-            final List<XmlElement> exampleElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_EXAMPLE );
-            for ( final XmlElement exampleElement : exampleElements )
-            {
-                final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute( exampleElement );
-                final String exampleString = macroMachine.expandMacros( exampleElement.getText() );
-                returnObj.add( new TemplateSetReference<>( exampleString, Collections.unmodifiableSet( definedTemplates ) ) );
-            }
-            if ( returnObj.isEmpty() )
-            {
-                returnObj.add( new TemplateSetReference<>( "", Collections.emptySet() ) );
-            }
-            return Collections.unmodifiableList( returnObj );
+            final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute( exampleElement );
+            final String exampleString = macroMachine.expandMacros( exampleElement.getText() );
+            returnObj.add( new TemplateSetReference<>( exampleString, Collections.unmodifiableSet( definedTemplates ) ) );
+        }
+        if ( returnObj.isEmpty() )
+        {
+            returnObj.add( new TemplateSetReference<>( "", Collections.emptySet() ) );
         }
+        return Collections.unmodifiableList( returnObj );
+    }
 
-        private static Map<PwmSettingProperty, String> readProperties( final PwmSetting pwmSetting )
+    private static Map<PwmSettingProperty, String> readProperties( final PwmSetting pwmSetting )
+    {
+        final Map<PwmSettingProperty, String> newProps = new EnumMap<>( PwmSettingProperty.class );
+        final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
+        final Optional<XmlElement> propertiesElement = settingElement.getChild( PwmSettingXml.XML_ELEMENT_PROPERTIES );
+        if ( propertiesElement.isPresent() )
         {
-            final Map<PwmSettingProperty, String> newProps = new EnumMap<>( PwmSettingProperty.class );
-            final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
-            final Optional<XmlElement> propertiesElement = settingElement.getChild( PwmSettingXml.XML_ELEMENT_PROPERTIES );
-            if ( propertiesElement.isPresent() )
+            final List<XmlElement> propertyElements = propertiesElement.get().getChildren( PwmSettingXml.XML_ELEMENT_PROPERTY );
+            if ( propertyElements != null )
             {
-                final List<XmlElement> propertyElements = propertiesElement.get().getChildren( PwmSettingXml.XML_ELEMENT_PROPERTY );
-                if ( propertyElements != null )
+                for ( final XmlElement propertyElement : propertyElements )
                 {
-                    for ( final XmlElement propertyElement : propertyElements )
+                    if ( propertyElement.getAttributeValue( PwmSettingXml.XML_ATTRIBUTE_KEY ) == null )
+                    {
+                        throw new IllegalStateException( "property element is missing 'key' attribute for value " + pwmSetting.getKey() );
+                    }
+                    final PwmSettingProperty property = JavaHelper.readEnumFromString(
+                            PwmSettingProperty.class,
+                            null,
+                            propertyElement.getAttributeValue( PwmSettingXml.XML_ATTRIBUTE_KEY ) );
+                    if ( property == null )
                     {
-                        if ( propertyElement.getAttributeValue( PwmSettingXml.XML_ATTRIBUTE_KEY ) == null )
-                        {
-                            throw new IllegalStateException( "property element is missing 'key' attribute for value " + pwmSetting.getKey() );
-                        }
-                        final PwmSettingProperty property = JavaHelper.readEnumFromString(
-                                PwmSettingProperty.class,
-                                null,
-                                propertyElement.getAttributeValue( PwmSettingXml.XML_ATTRIBUTE_KEY ) );
-                        if ( property == null )
-                        {
-                            throw new IllegalStateException( "property element has unknown 'key' attribute for value " + pwmSetting.getKey() );
-                        }
-                        newProps.put( property, propertyElement.getText() );
+                        throw new IllegalStateException( "property element has unknown 'key' attribute for value " + pwmSetting.getKey() );
                     }
+                    newProps.put( property, propertyElement.getText() );
                 }
             }
-            return Collections.unmodifiableMap( newProps );
         }
+        return Collections.unmodifiableMap( newProps );
+    }
 
-        private static List<TemplateSetReference<StoredValue>> readDefaultValue( final PwmSetting pwmSetting )
+    private static List<TemplateSetReference<StoredValue>> readDefaultValue( final PwmSetting pwmSetting )
+    {
+        final List<TemplateSetReference<StoredValue>> returnObj = new ArrayList<>();
+        final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
+        final List<XmlElement> defaultElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_DEFAULT );
+        if ( pwmSetting.getSyntax() == PwmSettingSyntax.PASSWORD )
         {
-            final List<TemplateSetReference<StoredValue>> returnObj = new ArrayList<>();
-            final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
-            final List<XmlElement> defaultElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_DEFAULT );
-            if ( pwmSetting.getSyntax() == PwmSettingSyntax.PASSWORD )
-            {
-                returnObj.add( new TemplateSetReference<>( new PasswordValue( null ), Collections.emptySet() ) );
-            }
-            else
-            {
-                for ( final XmlElement defaultElement : defaultElements )
-                {
-                    final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute( defaultElement );
-                    final StoredValue storedValue = ValueFactory.fromXmlValues( pwmSetting, defaultElement, null );
-                    returnObj.add( new TemplateSetReference<>( storedValue, definedTemplates ) );
-                }
-            }
-            if ( returnObj.isEmpty() )
+            returnObj.add( new TemplateSetReference<>( new PasswordValue( null ), Collections.emptySet() ) );
+        }
+        else
+        {
+            for ( final XmlElement defaultElement : defaultElements )
             {
-                throw new IllegalStateException( "no default value for setting " + pwmSetting.getKey() );
+                final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute( defaultElement );
+                final StoredValue storedValue = ValueFactory.fromXmlValues( pwmSetting, defaultElement, null );
+                returnObj.add( new TemplateSetReference<>( storedValue, definedTemplates ) );
             }
-            return Collections.unmodifiableList( returnObj );
         }
-
-
-        private static boolean readRequired( final PwmSetting pwmSetting )
+        if ( returnObj.isEmpty() )
         {
-            final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
-            final String requiredAttribute = settingElement.getAttributeValue( PwmSettingXml.XML_ELEMENT_REQUIRED );
-            return "true".equalsIgnoreCase( requiredAttribute );
+            throw new IllegalStateException( "no default value for setting " + pwmSetting.getKey() );
         }
+        return Collections.unmodifiableList( returnObj );
+    }
 
-        private static boolean readHidden( final PwmSetting pwmSetting )
-        {
-            final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
-            final String requiredAttribute = settingElement.getAttributeValue( PwmSettingXml.XML_ELEMENT_HIDDEN );
-            return "true".equalsIgnoreCase( requiredAttribute ) || pwmSetting.getCategory().isHidden();
-        }
 
-        private static int readLevel( final PwmSetting pwmSetting )
-        {
-            final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
-            final String levelAttribute = settingElement.getAttributeValue( PwmSettingXml.XML_ELEMENT_LEVEL );
-            return JavaHelper.silentParseInt( levelAttribute, 0 );
-        }
+    private static boolean readRequired( final PwmSetting pwmSetting )
+    {
+        final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
+        final String requiredAttribute = settingElement.getAttributeValue( PwmSettingXml.XML_ELEMENT_REQUIRED );
+        return "true".equalsIgnoreCase( requiredAttribute );
+    }
+
+    private static boolean readHidden( final PwmSetting pwmSetting )
+    {
+        final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
+        final String requiredAttribute = settingElement.getAttributeValue( PwmSettingXml.XML_ELEMENT_HIDDEN );
+        return "true".equalsIgnoreCase( requiredAttribute ) || pwmSetting.getCategory().isHidden();
+    }
+
+    private static int readLevel( final PwmSetting pwmSetting )
+    {
+        final XmlElement settingElement = PwmSettingXml.readSettingXml( pwmSetting );
+        final String levelAttribute = settingElement.getAttributeValue( PwmSettingXml.XML_ELEMENT_LEVEL );
+        return JavaHelper.silentParseInt( levelAttribute, 0 );
+    }
 
-        private static Pattern readPattern( final PwmSetting pwmSetting )
+    private static Pattern readPattern( final PwmSetting pwmSetting )
+    {
+        final XmlElement settingNode = PwmSettingXml.readSettingXml( pwmSetting );
+        final Optional<XmlElement> regexNode = settingNode.getChild( PwmSettingXml.XML_ELEMENT_REGEX );
+        if ( regexNode.isPresent() )
         {
-            final XmlElement settingNode = PwmSettingXml.readSettingXml( pwmSetting );
-            final Optional<XmlElement> regexNode = settingNode.getChild( PwmSettingXml.XML_ELEMENT_REGEX );
-            if ( regexNode.isPresent() )
+            try
             {
-                try
-                {
-                    return Pattern.compile( regexNode.get().getText() );
-                }
-                catch ( final PatternSyntaxException e )
-                {
-                    final String errorMsg = "error compiling regex constraints for setting " + pwmSetting.toString() + ", error: " + e.getMessage();
-                    LOGGER.error( () -> errorMsg, e );
-                    throw new IllegalStateException( errorMsg, e );
-                }
+                return Pattern.compile( regexNode.get().getText() );
+            }
+            catch ( final PatternSyntaxException e )
+            {
+                final String errorMsg = "error compiling regex constraints for setting " + pwmSetting.toString() + ", error: " + e.getMessage();
+                LOGGER.error( () -> errorMsg, e );
+                throw new IllegalStateException( errorMsg, e );
             }
-            return Pattern.compile( ".*", Pattern.DOTALL );
         }
+        return Pattern.compile( ".*", Pattern.DOTALL );
+    }
 
-        private static String readLabel( final PwmSetting pwmSetting, final Locale locale )
-        {
-            final String propertyKey = password.pwm.i18n.PwmSetting.SETTING_LABEL_PREFIX + pwmSetting.getKey();
-            return LocaleHelper.getLocalizedMessage( locale, propertyKey, null, password.pwm.i18n.PwmSetting.class );
-        }
+    private static String readMenuLocationDebugDefault( final PwmSetting pwmSetting )
+    {
+        final Locale locale = PwmConstants.DEFAULT_LOCALE;
+        final String separator = LocaleHelper.getLocalizedMessage( locale, Config.Display_SettingNavigationSeparator, null );
+        return pwmSetting.getCategory().toMenuLocationDebug( null, locale ) + separator + pwmSetting.getLabel( locale );
+    }
+
+    private static String readMenuLocationDebug( final PwmSetting pwmSetting, final String profileID, final Locale locale )
+    {
+        final String separator = LocaleHelper.getLocalizedMessage( locale, Config.Display_SettingNavigationSeparator, null );
+        return pwmSetting.getCategory().toMenuLocationDebug( profileID, locale ) + separator + pwmSetting.getLabel( locale );
     }
+
+    private static String readLabel( final PwmSetting pwmSetting, final Locale locale )
+    {
+        final String propertyKey = password.pwm.i18n.PwmSetting.SETTING_LABEL_PREFIX + pwmSetting.getKey();
+        return LocaleHelper.getLocalizedMessage( locale, propertyKey, null, password.pwm.i18n.PwmSetting.class );
+    }
+}
 }

+ 2 - 2
server/src/main/java/password/pwm/config/stored/StoredConfigData.java

@@ -56,14 +56,14 @@ class StoredConfigData
 
     static Map<StoredConfigItemKey, ValueMetaData> carrierAsMetaDataMap( final Collection<ValueAndMetaCarrier> input )
     {
-        return input.parallelStream()
+        return input.stream()
                 .filter( ( t ) -> t.getKey() != null && t.getMetaData() != null )
                 .collect( Collectors.toMap( StoredConfigData.ValueAndMetaCarrier::getKey, StoredConfigData.ValueAndMetaCarrier::getMetaData ) );
     }
 
     static Map<StoredConfigItemKey, StoredValue> carrierAsStoredValueMap( final Collection<ValueAndMetaCarrier> input )
     {
-        return input.parallelStream()
+        return input.stream()
                 .filter( ( t ) -> t.getKey() != null && t.getValue() != null )
                 .collect( Collectors.toMap( StoredConfigData.ValueAndMetaCarrier::getKey, StoredConfigData.ValueAndMetaCarrier::getValue ) );
     }

+ 48 - 13
server/src/main/java/password/pwm/config/stored/StoredConfigItemKey.java

@@ -27,15 +27,14 @@ import password.pwm.i18n.Config;
 import password.pwm.i18n.PwmLocaleBundle;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.JavaHelper;
-import password.pwm.util.java.LazySupplier;
 import password.pwm.util.java.StringUtil;
 
 import java.io.Serializable;
 import java.util.Collections;
+import java.util.Comparator;
 import java.util.Locale;
 import java.util.Objects;
 import java.util.Set;
-import java.util.function.Supplier;
 import java.util.stream.Collectors;
 
 public class StoredConfigItemKey implements Serializable, Comparable<StoredConfigItemKey>
@@ -63,10 +62,10 @@ public class StoredConfigItemKey implements Serializable, Comparable<StoredConfi
     private final String recordID;
     private final String profileID;
 
-    private final transient Supplier<String> toStringSupplier = new LazySupplier<>( () -> this.getLabel( PwmConstants.DEFAULT_LOCALE ) );
-
     private static final long serialVersionUID = 1L;
 
+
+
     private StoredConfigItemKey( final RecordType recordType, final String recordID, final String profileID )
     {
         Objects.requireNonNull( recordType, "recordType can not be null" );
@@ -223,32 +222,40 @@ public class StoredConfigItemKey implements Serializable, Comparable<StoredConfi
     }
 
     @Override
-    public int hashCode()
+    public boolean equals( final Object o )
     {
-        return toString().hashCode();
+        if ( this == o )
+        {
+            return true;
+        }
+        if ( o == null || getClass() != o.getClass() )
+        {
+            return false;
+        }
+        final StoredConfigItemKey that = ( StoredConfigItemKey ) o;
+        return Objects.equals( recordType, that.recordType )
+                && Objects.equals( recordID, that.recordID )
+                && Objects.equals( profileID, that.profileID );
     }
 
     @Override
-    public boolean equals( final Object anotherObject )
+    public int hashCode()
     {
-        return anotherObject instanceof StoredConfigItemKey
-                && toString().equals( anotherObject.toString() );
+        return Objects.hash( recordType, recordID, profileID );
     }
 
     @Override
     public String toString()
     {
-        return toStringSupplier.get();
+        return getLabel( PwmConstants.DEFAULT_LOCALE );
     }
 
     @Override
     public int compareTo( final StoredConfigItemKey o )
     {
-        return toString().compareTo( o.toString() );
+        return comparator( PwmConstants.DEFAULT_LOCALE ).compare( this, o );
     }
 
-
-
     public PwmSettingSyntax getSyntax()
     {
         switch ( getRecordType() )
@@ -285,4 +292,32 @@ public class StoredConfigItemKey implements Serializable, Comparable<StoredConfi
 
         return Collections.unmodifiableSet( input.stream().filter( ( k ) -> k.isRecordType( recordType ) ).collect( Collectors.toSet() ) );
     }
+
+    private static Comparator<StoredConfigItemKey> comparator( final Locale locale )
+    {
+        final Comparator<StoredConfigItemKey> typeComparator = Comparator.comparing(
+                StoredConfigItemKey::getRecordType,
+                Comparator.nullsLast( Comparator.naturalOrder() ) );
+
+        final Comparator<StoredConfigItemKey> recordComparator = ( o1, o2 ) ->
+        {
+            if ( Objects.equals( o1.getRecordType(), o2.getRecordType() )
+                    && o1.isRecordType( RecordType.SETTING ) )
+            {
+                final Comparator<PwmSetting> pwmSettingComparator = PwmSetting.menuLocationComparator( locale );
+                return pwmSettingComparator.compare( o1.toPwmSetting(), o2.toPwmSetting() );
+            }
+            else
+            {
+                return o1.getRecordID().compareTo( o2.getRecordID() );
+            }
+        };
+
+        final Comparator<StoredConfigItemKey> profileComparator = Comparator.comparing( StoredConfigItemKey::getProfileID,
+                Comparator.nullsLast( Comparator.naturalOrder() ) );
+
+        return typeComparator.thenComparing( recordComparator ).thenComparing( profileComparator );
+    }
+
+
 }

+ 6 - 1
server/src/main/java/password/pwm/svc/stats/StatisticsBundle.java

@@ -20,6 +20,7 @@
 
 package password.pwm.svc.stats;
 
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
@@ -126,9 +127,13 @@ public class StatisticsBundle
 
     private static class AverageBean implements Serializable
     {
+        private static final long serialVersionUID = 1L;
+
         private BigInteger total = BigInteger.ZERO;
         private BigInteger count = BigInteger.ZERO;
-        private final Lock lock = new ReentrantLock();
+
+        @SuppressFBWarnings( "SE_TRANSIENT_FIELD_NOT_RESTORED" )
+        private final transient Lock lock = new ReentrantLock();
 
         AverageBean( )
         {

+ 2 - 1
server/src/main/java/password/pwm/util/java/AverageTracker.java

@@ -32,7 +32,8 @@ public class AverageTracker
 {
     private final int maxSamples;
     private final Queue<BigInteger> samples = new ArrayDeque<>();
-    private final ReadWriteLock lock = new ReentrantReadWriteLock();
+
+    private final transient ReadWriteLock lock = new ReentrantReadWriteLock();
 
     public AverageTracker( final int maxSamples )
     {

+ 19 - 2
server/src/main/java/password/pwm/util/java/StringUtil.java

@@ -352,7 +352,17 @@ public abstract class StringUtil
         }
     }
 
-    public static String padEndToLength( final String input, final int length, final char appendChar )
+    public static String padRight( final String input, final int length, final char appendChar )
+    {
+        return padImpl( input, length, appendChar, true );
+    }
+
+    public static String padLeft( final String input, final int length, final char appendChar )
+    {
+        return padImpl( input, length, appendChar, false );
+    }
+
+    private static String padImpl( final String input, final int length, final char appendChar, final boolean right )
     {
         if ( input == null )
         {
@@ -367,7 +377,14 @@ public abstract class StringUtil
         final StringBuilder sb = new StringBuilder( input );
         while ( sb.length() < length )
         {
-            sb.append( appendChar );
+            if ( right )
+            {
+                sb.append( appendChar );
+            }
+            else
+            {
+                sb.insert( 0, appendChar );
+            }
         }
 
         return sb.toString();

+ 1 - 1
server/src/main/java/password/pwm/util/localdb/LocalDBStoredQueue.java

@@ -390,7 +390,7 @@ public class LocalDBStoredQueue implements Queue<String>, Deque<String>
         try
         {
             final List<String> values = internalQueue.getFirst( 1 );
-            if ( values == null || values.isEmpty() )
+            if ( JavaHelper.isEmpty( values ) )
             {
                 return null;
             }

+ 33 - 26
server/src/main/java/password/pwm/util/localdb/WorkQueueProcessor.java

@@ -245,40 +245,47 @@ public final class WorkQueueProcessor<W extends Serializable>
     private void submitToQueue( final ItemWrapper<W> itemWrapper )
             throws PwmOperationalException
     {
-        submitLock.lock();
-        try
+        if ( workerThread == null )
         {
-            if ( workerThread == null )
-            {
-                final String errorMsg = this.getClass().getName() + " has been closed, unable to submit new item";
-                throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_INTERNAL, errorMsg ) );
-            }
+            final String errorMsg = this.getClass().getName() + " has been closed, unable to submit new item";
+            throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_INTERNAL, errorMsg ) );
+        }
 
-            final String asString = JsonUtil.serialize( itemWrapper );
+        if ( settings.getMaxEvents() <= 0 )
+        {
+            return;
+        }
 
-            if ( settings.getMaxEvents() > 0 )
+        final Instant startTime = Instant.now();
+        int attempts = 1;
+
+        final String asString = JsonUtil.serialize( itemWrapper );
+        while ( !queue.offerLast( asString ) )
+        {
+            attempts++;
+            final TimeDuration waitTime = TimeDuration.fromCurrent( startTime );
+            if ( waitTime.isLongerThan( settings.getMaxSubmitWaitTime() ) )
             {
-                final Instant startTime = Instant.now();
-                while ( !queue.offerLast( asString ) )
-                {
-                    if ( TimeDuration.fromCurrent( startTime ).isLongerThan( settings.getMaxSubmitWaitTime() ) )
-                    {
-                        final String errorMsg = "unable to submit item to worker queue after " + settings.getMaxSubmitWaitTime().asCompactString()
-                                + ", item=" + itemProcessor.convertToDebugString( itemWrapper.getWorkItem() );
-                        throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_INTERNAL, errorMsg ) );
-                    }
-                    SUBMIT_QUEUE_FULL_RETRY_CYCLE_INTERVAL.pause();
-                }
+                final String errorMsg = "unable to submit item to worker queue after " + waitTime.asCompactString()
+                        + " and " + attempts + " attempts, item=" + itemProcessor.convertToDebugString( itemWrapper.getWorkItem() );
+                throw new PwmOperationalException( new ErrorInformation( PwmError.ERROR_INTERNAL, errorMsg ) );
+            }
+            SUBMIT_QUEUE_FULL_RETRY_CYCLE_INTERVAL.pause();
+        }
 
-                eldestItem = itemWrapper.getDate();
-                workerThread.notifyWorkPending();
+        eldestItem = itemWrapper.getDate();
+        workerThread.notifyWorkPending();
 
-                logger.trace( () -> "item submitted: " + makeDebugText( itemWrapper ) );
-            }
+        if ( attempts > 1 )
+        {
+            logger.trace( () -> "item submitted directly to queue: " + makeDebugText( itemWrapper ),
+                    () -> TimeDuration.fromCurrent( startTime ) );
         }
-        finally
+        else
         {
-            submitLock.unlock();
+            final int finalAttempts = attempts;
+            logger.debug( () -> "item submitted to queue after " + finalAttempts + " attempts: "
+                    + makeDebugText( itemWrapper ), () -> TimeDuration.fromCurrent( startTime ) );
         }
     }
 

+ 1 - 1
server/src/main/java/password/pwm/util/logging/PwmLogEvent.java

@@ -212,7 +212,7 @@ public class PwmLogEvent implements Serializable, Comparable<PwmLogEvent>
             sb.append( this.getTimestamp().toString() );
             sb.append( ", " );
         }
-        sb.append( StringUtil.padEndToLength( getLevel().toString(), 5, ' ' ) );
+        sb.append( StringUtil.padRight( getLevel().toString(), 5, ' ' ) );
         sb.append( ", " );
         sb.append( shortenTopic( this.topic ) );
         sb.append( ", " );

+ 7 - 0
server/src/test/java/password/pwm/config/PwmSettingTest.java

@@ -197,4 +197,11 @@ public class PwmSettingTest
             seenKeys.add( pwmSetting.getKey() );
         }
     }
+
+    @Test
+    public void sortedByMenuLocation()
+    {
+        final Set<PwmSetting> sortedSet = PwmSetting.sortedByMenuLocation( PwmConstants.DEFAULT_LOCALE );
+        Assert.assertEquals( sortedSet.size(), PwmSetting.values().length );
+    }
 }

+ 46 - 0
server/src/test/java/password/pwm/config/stored/StoredConfigItemKeyTest.java

@@ -26,8 +26,12 @@ import password.pwm.config.PwmSetting;
 import password.pwm.i18n.Config;
 import password.pwm.i18n.Display;
 import password.pwm.i18n.PwmLocaleBundle;
+import password.pwm.util.java.StringUtil;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 public class StoredConfigItemKeyTest
@@ -57,7 +61,49 @@ public class StoredConfigItemKeyTest
             Assert.assertEquals( 1, set.size() );
             set.add( StoredConfigItemKey.fromLocaleBundle( PwmLocaleBundle.CONFIG, Config.Display_AboutTemplates.getKey() ) );
             Assert.assertEquals( 2, set.size() );
+        }
+    }
+
+    @Test
+    public void testKeyUniqueness()
+    {
+        final Set<StoredConfigItemKey> set = new HashSet<>();
+        set.add( StoredConfigItemKey.fromSetting( PwmSetting.PWM_SITE_URL, null ) );
+        set.add( StoredConfigItemKey.fromSetting( PwmSetting.SECURITY_ENABLE_FORM_NONCE, null ) );
+        set.add( StoredConfigItemKey.fromSetting( PwmSetting.SECURITY_ENABLE_FORM_NONCE, null ) );
+        set.add( StoredConfigItemKey.fromSetting( PwmSetting.CHALLENGE_ENABLE, null ) );
+        Assert.assertEquals( 3, set.size() );
+    }
+
 
+    @Test
+    public void testKeySorting()
+    {
+        final List<String> profiles = new ArrayList<>();
+        for ( int i = 0; i < 20; i++ )
+        {
+            profiles.add( "profile" + StringUtil.padLeft( String.valueOf( i ), 2, '0' ) );
         }
+
+        final List<StoredConfigItemKey> list = new ArrayList<>();
+        for ( final PwmSetting pwmSetting : PwmSetting.values() )
+        {
+            if ( pwmSetting.getCategory().hasProfiles() )
+            {
+                for ( final String profileID : profiles )
+                {
+                    list.add( StoredConfigItemKey.fromSetting( pwmSetting, profileID ) );
+                }
+            }
+            else
+            {
+                list.add( StoredConfigItemKey.fromSetting( pwmSetting, null ) );
+            }
+        }
+
+        Collections.shuffle( list );
+        Collections.sort( list );
+        System.out.println( list.size() );
+        //list.forEach( System.out::println );
     }
 }