Browse Source

Merge branch 'xml-abstraction'

jrivard@gmail.com 6 years ago
parent
commit
50d5d7d4ad
44 changed files with 1516 additions and 680 deletions
  1. 4 0
      build/checkstyle-header.xml
  2. 28 0
      build/checkstyle-suppression.xml
  3. 3 0
      build/checkstyle.xml
  4. 35 36
      server/src/main/java/password/pwm/config/PwmSetting.java
  5. 9 10
      server/src/main/java/password/pwm/config/PwmSettingCategory.java
  6. 6 7
      server/src/main/java/password/pwm/config/PwmSettingTemplate.java
  7. 19 32
      server/src/main/java/password/pwm/config/PwmSettingXml.java
  8. 3 3
      server/src/main/java/password/pwm/config/StoredValue.java
  9. 77 119
      server/src/main/java/password/pwm/config/stored/ConfigurationCleaner.java
  10. 2 6
      server/src/main/java/password/pwm/config/stored/ConfigurationReader.java
  11. 27 19
      server/src/main/java/password/pwm/config/stored/NGStoredConfigurationFactory.java
  12. 115 109
      server/src/main/java/password/pwm/config/stored/StoredConfigurationImpl.java
  13. 3 11
      server/src/main/java/password/pwm/config/stored/StoredConfigurationUtil.java
  14. 2 16
      server/src/main/java/password/pwm/config/stored/ValueMetaData.java
  15. 11 10
      server/src/main/java/password/pwm/config/value/ActionValue.java
  16. 7 6
      server/src/main/java/password/pwm/config/value/BooleanValue.java
  17. 10 11
      server/src/main/java/password/pwm/config/value/ChallengeValue.java
  18. 10 10
      server/src/main/java/password/pwm/config/value/CustomLinkValue.java
  19. 12 11
      server/src/main/java/password/pwm/config/value/EmailValue.java
  20. 14 15
      server/src/main/java/password/pwm/config/value/FileValue.java
  21. 10 10
      server/src/main/java/password/pwm/config/value/FormValue.java
  22. 10 11
      server/src/main/java/password/pwm/config/value/LocalizedStringArrayValue.java
  23. 9 10
      server/src/main/java/password/pwm/config/value/LocalizedStringValue.java
  24. 18 17
      server/src/main/java/password/pwm/config/value/NamedSecretValue.java
  25. 9 8
      server/src/main/java/password/pwm/config/value/NumericArrayValue.java
  26. 7 6
      server/src/main/java/password/pwm/config/value/NumericValue.java
  27. 9 9
      server/src/main/java/password/pwm/config/value/OptionListValue.java
  28. 10 9
      server/src/main/java/password/pwm/config/value/PasswordValue.java
  29. 13 13
      server/src/main/java/password/pwm/config/value/PrivateKeyValue.java
  30. 9 9
      server/src/main/java/password/pwm/config/value/RemoteWebServiceValue.java
  31. 11 12
      server/src/main/java/password/pwm/config/value/StringArrayValue.java
  32. 7 7
      server/src/main/java/password/pwm/config/value/StringValue.java
  33. 9 9
      server/src/main/java/password/pwm/config/value/UserPermissionValue.java
  34. 2 2
      server/src/main/java/password/pwm/config/value/ValueFactory.java
  35. 7 7
      server/src/main/java/password/pwm/config/value/VerificationMethodValue.java
  36. 9 8
      server/src/main/java/password/pwm/config/value/X509CertificateValue.java
  37. 31 91
      server/src/main/java/password/pwm/util/java/LicenseInfoReader.java
  38. 5 0
      server/src/main/java/password/pwm/util/java/TimeDuration.java
  39. 150 0
      server/src/main/java/password/pwm/util/java/XmlDocument.java
  40. 396 0
      server/src/main/java/password/pwm/util/java/XmlElement.java
  41. 252 0
      server/src/main/java/password/pwm/util/java/XmlFactory.java
  42. 46 0
      server/src/test/java/password/pwm/util/java/XmlFactoryTest.java
  43. 84 0
      server/src/test/resources/password/pwm/util/java/XmlFactoryTest.xml
  44. 6 11
      webapp/src/main/webapp/public/reference/license.jsp

+ 4 - 0
build/checkstyle-header.xml

@@ -31,6 +31,10 @@
 
 
 <module name="Checker">
 <module name="Checker">
 
 
+    <module name="SuppressionFilter">
+        <property name="file" value="${basedir}/build/checkstyle-suppression.xml" />
+    </module>
+
     <module name="Header">
     <module name="Header">
         <property name="headerFile" value="${basedir}/build/license-header-jsp.txt"/>
         <property name="headerFile" value="${basedir}/build/license-header-jsp.txt"/>
         <property name="fileExtensions" value="jsp"/>
         <property name="fileExtensions" value="jsp"/>

+ 28 - 0
build/checkstyle-suppression.xml

@@ -0,0 +1,28 @@
+<!--
+  ~ Password Management Servlets (PWM)
+  ~ http://www.pwm-project.org
+  ~
+  ~ Copyright (c) 2006-2009 Novell, Inc.
+  ~ Copyright (c) 2009-2016 The PWM Project
+  ~
+  ~ This program is free software; you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation; either version 2 of the License, or
+  ~ (at your option) any later version.
+  ~
+  ~ This program is distributed in the hope that it will be useful,
+  ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+  ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  ~ GNU General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program; if not, write to the Free Software
+  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+  -->
+<!DOCTYPE suppressions PUBLIC
+        "-//Puppy Crawl//DTD Suppressions 1.1//EN"
+        "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
+
+<suppressions>
+    <suppress files="XmlFactoryTest\.xml" checks="[a-zA-Z0-9]*"/>
+</suppressions>

+ 3 - 0
build/checkstyle.xml

@@ -31,6 +31,9 @@
 
 
 <module name="Checker">
 <module name="Checker">
 
 
+    <module name="SuppressionFilter">
+        <property name="file" value="${basedir}/build/checkstyle-suppression.xml" />
+    </module>
 
 
     <!-- Checks that each Java package has a Javadoc file used for commenting. -->
     <!-- Checks that each Java package has a Javadoc file used for commenting. -->
     <!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage       -->
     <!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage       -->

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

@@ -22,14 +22,13 @@
 
 
 package password.pwm.config;
 package password.pwm.config;
 
 
-import org.jdom2.Attribute;
-import org.jdom2.Element;
 import password.pwm.config.value.PasswordValue;
 import password.pwm.config.value.PasswordValue;
 import password.pwm.config.value.ValueFactory;
 import password.pwm.config.value.ValueFactory;
 import password.pwm.i18n.Config;
 import password.pwm.i18n.Config;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.XmlElement;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.macro.MacroMachine;
 import password.pwm.util.macro.MacroMachine;
 
 
@@ -1314,15 +1313,15 @@ public enum PwmSetting
         if ( defaultValues == null )
         if ( defaultValues == null )
         {
         {
             final List<TemplateSetAssociation> returnObj = new ArrayList<>();
             final List<TemplateSetAssociation> returnObj = new ArrayList<>();
-            final Element settingElement = PwmSettingXml.readSettingXml( this );
-            final List<Element> defaultElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_DEFAULT );
+            final XmlElement settingElement = PwmSettingXml.readSettingXml( this );
+            final List<XmlElement> defaultElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_DEFAULT );
             if ( this.getSyntax() == PwmSettingSyntax.PASSWORD )
             if ( this.getSyntax() == PwmSettingSyntax.PASSWORD )
             {
             {
                 returnObj.add( new TemplateSetAssociation( new PasswordValue( null ), Collections.emptySet() ) );
                 returnObj.add( new TemplateSetAssociation( new PasswordValue( null ), Collections.emptySet() ) );
             }
             }
             else
             else
             {
             {
-                for ( final Element defaultElement : defaultElements )
+                for ( final XmlElement defaultElement : defaultElements )
                 {
                 {
                     final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute( defaultElement );
                     final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute( defaultElement );
                     final StoredValue storedValue = ValueFactory.fromXmlValues( this, defaultElement, null );
                     final StoredValue storedValue = ValueFactory.fromXmlValues( this, defaultElement, null );
@@ -1363,20 +1362,20 @@ public enum PwmSetting
         if ( options == null )
         if ( options == null )
         {
         {
             final Map<String, String> returnList = new LinkedHashMap<>();
             final Map<String, String> returnList = new LinkedHashMap<>();
-            final Element settingElement = PwmSettingXml.readSettingXml( this );
-            final Element optionsElement = settingElement.getChild( "options" );
+            final XmlElement settingElement = PwmSettingXml.readSettingXml( this );
+            final XmlElement optionsElement = settingElement.getChild( "options" );
             if ( optionsElement != null )
             if ( optionsElement != null )
             {
             {
-                final List<Element> optionElements = optionsElement.getChildren( "option" );
+                final List<XmlElement> optionElements = optionsElement.getChildren( "option" );
                 if ( optionElements != null )
                 if ( optionElements != null )
                 {
                 {
-                    for ( final Element optionElement : optionElements )
+                    for ( final XmlElement optionElement : optionElements )
                     {
                     {
-                        if ( optionElement.getAttribute( "value" ) == null )
+                        if ( optionElement.getAttributeValue( "value" ) == null )
                         {
                         {
                             throw new IllegalStateException( "option element is missing 'value' attribute for key " + this.getKey() );
                             throw new IllegalStateException( "option element is missing 'value' attribute for key " + this.getKey() );
                         }
                         }
-                        returnList.put( optionElement.getAttribute( "value" ).getValue(), optionElement.getValue() );
+                        returnList.put( optionElement.getAttributeValue( "value" ), optionElement.getText() );
                     }
                     }
                 }
                 }
             }
             }
@@ -1392,14 +1391,14 @@ public enum PwmSetting
         if ( properties == null )
         if ( properties == null )
         {
         {
             final Map<PwmSettingProperty, String> newProps = new LinkedHashMap<>();
             final Map<PwmSettingProperty, String> newProps = new LinkedHashMap<>();
-            final Element settingElement = PwmSettingXml.readSettingXml( this );
-            final Element propertiesElement = settingElement.getChild( "properties" );
+            final XmlElement settingElement = PwmSettingXml.readSettingXml( this );
+            final XmlElement propertiesElement = settingElement.getChild( "properties" );
             if ( propertiesElement != null )
             if ( propertiesElement != null )
             {
             {
-                final List<Element> propertyElements = propertiesElement.getChildren( "property" );
+                final List<XmlElement> propertyElements = propertiesElement.getChildren( "property" );
                 if ( propertyElements != null )
                 if ( propertyElements != null )
                 {
                 {
-                    for ( final Element propertyElement : propertyElements )
+                    for ( final XmlElement propertyElement : propertyElements )
                     {
                     {
                         if ( propertyElement.getAttributeValue( "key" ) == null )
                         if ( propertyElement.getAttributeValue( "key" ) == null )
                         {
                         {
@@ -1410,7 +1409,7 @@ public enum PwmSetting
                         {
                         {
                             throw new IllegalStateException( "property element has unknown 'key' attribute for value " + this.getKey() );
                             throw new IllegalStateException( "property element has unknown 'key' attribute for value " + this.getKey() );
                         }
                         }
-                        newProps.put( property, propertyElement.getValue() );
+                        newProps.put( property, propertyElement.getText() );
                     }
                     }
                 }
                 }
             }
             }
@@ -1426,9 +1425,9 @@ public enum PwmSetting
         if ( flags == null )
         if ( flags == null )
         {
         {
             final Collection<PwmSettingFlag> returnObj = new ArrayList<>();
             final Collection<PwmSettingFlag> returnObj = new ArrayList<>();
-            final Element settingElement = PwmSettingXml.readSettingXml( this );
-            final List<Element> flagElements = settingElement.getChildren( "flag" );
-            for ( final Element flagElement : flagElements )
+            final XmlElement settingElement = PwmSettingXml.readSettingXml( this );
+            final List<XmlElement> flagElements = settingElement.getChildren( "flag" );
+            for ( final XmlElement flagElement : flagElements )
             {
             {
                 final String value = flagElement.getTextTrim();
                 final String value = flagElement.getTextTrim();
 
 
@@ -1453,12 +1452,12 @@ public enum PwmSetting
     {
     {
         if ( ldapPermissionInfo == null )
         if ( ldapPermissionInfo == null )
         {
         {
-            final Element settingElement = PwmSettingXml.readSettingXml( this );
-            final List<Element> permissionElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_LDAP_PERMISSION );
+            final XmlElement settingElement = PwmSettingXml.readSettingXml( this );
+            final List<XmlElement> permissionElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_LDAP_PERMISSION );
             final List<LDAPPermissionInfo> returnObj = new ArrayList<>();
             final List<LDAPPermissionInfo> returnObj = new ArrayList<>();
             if ( permissionElements != null )
             if ( permissionElements != null )
             {
             {
-                for ( final Element permissionElement : permissionElements )
+                for ( final XmlElement permissionElement : permissionElements )
                 {
                 {
                     final LDAPPermissionInfo.Actor actor = JavaHelper.readEnumFromString(
                     final LDAPPermissionInfo.Actor actor = JavaHelper.readEnumFromString(
                             LDAPPermissionInfo.Actor.class,
                             LDAPPermissionInfo.Actor.class,
@@ -1504,9 +1503,9 @@ public enum PwmSetting
         {
         {
             final List<TemplateSetAssociation> returnObj = new ArrayList<>();
             final List<TemplateSetAssociation> returnObj = new ArrayList<>();
             final MacroMachine macroMachine = MacroMachine.forStatic();
             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 XmlElement settingElement = PwmSettingXml.readSettingXml( this );
+            final List<XmlElement> exampleElements = settingElement.getChildren( PwmSettingXml.XML_ELEMENT_EXAMPLE );
+            for ( final XmlElement exampleElement : exampleElements )
             {
             {
                 final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute( exampleElement );
                 final Set<PwmSettingTemplate> definedTemplates = PwmSettingXml.parseTemplateAttribute( exampleElement );
                 final String exampleString = macroMachine.expandMacros( exampleElement.getText() );
                 final String exampleString = macroMachine.expandMacros( exampleElement.getText() );
@@ -1527,9 +1526,9 @@ public enum PwmSetting
     {
     {
         if ( required == null )
         if ( required == null )
         {
         {
-            final Element settingElement = PwmSettingXml.readSettingXml( this );
-            final Attribute requiredAttribute = settingElement.getAttribute( "required" );
-            final boolean requiredOutput = requiredAttribute != null && "true".equalsIgnoreCase( requiredAttribute.getValue() );
+            final XmlElement settingElement = PwmSettingXml.readSettingXml( this );
+            final String requiredAttribute = settingElement.getAttributeValue( "required" );
+            final boolean requiredOutput = requiredAttribute != null && "true".equalsIgnoreCase( requiredAttribute );
             required = ( ) -> requiredOutput;
             required = ( ) -> requiredOutput;
         }
         }
         return required.get();
         return required.get();
@@ -1539,9 +1538,9 @@ public enum PwmSetting
     {
     {
         if ( hidden == null )
         if ( hidden == null )
         {
         {
-            final Element settingElement = PwmSettingXml.readSettingXml( this );
-            final Attribute requiredAttribute = settingElement.getAttribute( "hidden" );
-            final boolean outputHidden = requiredAttribute != null && "true".equalsIgnoreCase( requiredAttribute.getValue() ) || this.getCategory().isHidden();
+            final XmlElement settingElement = PwmSettingXml.readSettingXml( this );
+            final String requiredAttribute = settingElement.getAttributeValue( "hidden" );
+            final boolean outputHidden = requiredAttribute != null && "true".equalsIgnoreCase( requiredAttribute ) || this.getCategory().isHidden();
             hidden = ( ) -> outputHidden;
             hidden = ( ) -> outputHidden;
         }
         }
         return hidden.get();
         return hidden.get();
@@ -1551,9 +1550,9 @@ public enum PwmSetting
     {
     {
         if ( level == null )
         if ( level == null )
         {
         {
-            final Element settingElement = PwmSettingXml.readSettingXml( this );
-            final Attribute levelAttribute = settingElement.getAttribute( "level" );
-            final int outputLevel = levelAttribute != null ? Integer.parseInt( levelAttribute.getValue() ) : 0;
+            final XmlElement settingElement = PwmSettingXml.readSettingXml( this );
+            final String levelAttribute = settingElement.getAttributeValue( "level" );
+            final int outputLevel = levelAttribute != null ? Integer.parseInt( levelAttribute ) : 0;
             level = ( ) -> outputLevel;
             level = ( ) -> outputLevel;
         }
         }
         return level.get();
         return level.get();
@@ -1563,8 +1562,8 @@ public enum PwmSetting
     {
     {
         if ( pattern == null )
         if ( pattern == null )
         {
         {
-            final Element settingNode = PwmSettingXml.readSettingXml( this );
-            final Element regexNode = settingNode.getChild( "regex" );
+            final XmlElement settingNode = PwmSettingXml.readSettingXml( this );
+            final XmlElement regexNode = settingNode.getChild( "regex" );
             if ( regexNode != null )
             if ( regexNode != null )
             {
             {
                 try
                 try

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

@@ -22,10 +22,9 @@
 
 
 package password.pwm.config;
 package password.pwm.config;
 
 
-import org.jdom2.Attribute;
-import org.jdom2.Element;
 import password.pwm.i18n.Config;
 import password.pwm.i18n.Config;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
+import password.pwm.util.java.XmlElement;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collection;
@@ -247,9 +246,9 @@ public enum PwmSettingCategory
     {
     {
         if ( level == null )
         if ( level == null )
         {
         {
-            final Element settingElement = PwmSettingXml.readCategoryXml( this );
-            final Attribute levelAttribute = settingElement.getAttribute( "level" );
-            final int output = levelAttribute != null ? Integer.parseInt( levelAttribute.getValue() ) : 0;
+            final XmlElement settingElement = PwmSettingXml.readCategoryXml( this );
+            final String levelAttribute = settingElement.getAttributeValue( "level" );
+            final int output = levelAttribute != null ? Integer.parseInt( levelAttribute ) : 0;
             level = ( ) -> output;
             level = ( ) -> output;
         }
         }
         return level.get();
         return level.get();
@@ -259,9 +258,9 @@ public enum PwmSettingCategory
     {
     {
         if ( hidden == null )
         if ( hidden == null )
         {
         {
-            final Element settingElement = PwmSettingXml.readCategoryXml( this );
-            final Attribute hiddenElement = settingElement.getAttribute( "hidden" );
-            if ( hiddenElement != null && "true".equalsIgnoreCase( hiddenElement.getValue() ) )
+            final XmlElement settingElement = PwmSettingXml.readCategoryXml( this );
+            final String hiddenElement = settingElement.getAttributeValue( "hidden" );
+            if ( hiddenElement != null && "true".equalsIgnoreCase( hiddenElement ) )
             {
             {
                 hidden = () -> true;
                 hidden = () -> true;
             }
             }
@@ -318,8 +317,8 @@ public enum PwmSettingCategory
         PwmSettingCategory nextCategory = this;
         PwmSettingCategory nextCategory = this;
         while ( nextCategory != null )
         while ( nextCategory != null )
         {
         {
-            final Element categoryElement = PwmSettingXml.readCategoryXml( nextCategory );
-            final Element profileElement = categoryElement.getChild( "profile" );
+            final XmlElement categoryElement = PwmSettingXml.readCategoryXml( nextCategory );
+            final XmlElement profileElement = categoryElement.getChild( "profile" );
             if ( profileElement != null )
             if ( profileElement != null )
             {
             {
                 final String settingKey = profileElement.getAttributeValue( "setting" );
                 final String settingKey = profileElement.getAttributeValue( "setting" );

+ 6 - 7
server/src/main/java/password/pwm/config/PwmSettingTemplate.java

@@ -22,9 +22,8 @@
 
 
 package password.pwm.config;
 package password.pwm.config;
 
 
-import org.jdom2.Attribute;
-import org.jdom2.Element;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.XmlElement;
 
 
 import java.util.EnumMap;
 import java.util.EnumMap;
 import java.util.Map;
 import java.util.Map;
@@ -65,14 +64,14 @@ public enum PwmSettingTemplate
 
 
     public boolean isHidden( )
     public boolean isHidden( )
     {
     {
-        final Element templateElement = readTemplateElement( this );
-        final Attribute requiredAttribute = templateElement.getAttribute( "hidden" );
-        return requiredAttribute != null && "true".equalsIgnoreCase( requiredAttribute.getValue() );
+        final XmlElement templateElement = readTemplateElement( this );
+        final String requiredAttribute = templateElement.getAttributeValue( "hidden" );
+        return requiredAttribute != null && "true".equalsIgnoreCase( requiredAttribute );
     }
     }
 
 
-    private static Element readTemplateElement( final PwmSettingTemplate pwmSettingTemplate )
+    private static XmlElement readTemplateElement( final PwmSettingTemplate pwmSettingTemplate )
     {
     {
-        final Element element = PwmSettingXml.readTemplateXml( pwmSettingTemplate );
+        final XmlElement element = PwmSettingXml.readTemplateXml( pwmSettingTemplate );
         if ( element == null )
         if ( element == null )
         {
         {
             throw new IllegalStateException( "missing PwmSetting.xml template element for " + pwmSettingTemplate );
             throw new IllegalStateException( "missing PwmSetting.xml template element for " + pwmSettingTemplate );

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

@@ -22,13 +22,11 @@
 
 
 package password.pwm.config;
 package password.pwm.config;
 
 
-import org.jdom2.Document;
-import org.jdom2.Element;
-import org.jdom2.JDOMException;
-import org.jdom2.input.SAXBuilder;
-import org.jdom2.xpath.XPathExpression;
-import org.jdom2.xpath.XPathFactory;
+import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
+import password.pwm.util.java.XmlDocument;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
 import javax.xml.XMLConstants;
 import javax.xml.XMLConstants;
@@ -36,7 +34,6 @@ import javax.xml.transform.stream.StreamSource;
 import javax.xml.validation.Schema;
 import javax.xml.validation.Schema;
 import javax.xml.validation.SchemaFactory;
 import javax.xml.validation.SchemaFactory;
 import javax.xml.validation.Validator;
 import javax.xml.validation.Validator;
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStream;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.Collections;
 import java.util.Collections;
@@ -59,21 +56,19 @@ public class PwmSettingXml
 
 
     private static final PwmLogger LOGGER = PwmLogger.forClass( PwmSettingXml.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( PwmSettingXml.class );
 
 
-    private static Document xmlDocCache;
+    private static XmlDocument xmlDocCache;
     private static final AtomicInteger LOAD_COUNTER = new AtomicInteger( 0 );
     private static final AtomicInteger LOAD_COUNTER = new AtomicInteger( 0 );
 
 
-    private static Document readXml( )
+    private static XmlDocument readXml( )
     {
     {
-        final Document docRefCopy = xmlDocCache;
+        final XmlDocument docRefCopy = xmlDocCache;
         if ( docRefCopy == null )
         if ( docRefCopy == null )
         {
         {
-            //validateXmlSchema();
             final InputStream inputStream = PwmSetting.class.getClassLoader().getResourceAsStream( SETTING_XML_FILENAME );
             final InputStream inputStream = PwmSetting.class.getClassLoader().getResourceAsStream( SETTING_XML_FILENAME );
-            final SAXBuilder builder = new SAXBuilder();
             try
             try
             {
             {
                 final Instant startTime = Instant.now();
                 final Instant startTime = Instant.now();
-                final Document newDoc = builder.build( inputStream );
+                final XmlDocument newDoc = XmlFactory.getFactory().parseXml( inputStream );
                 final TimeDuration parseDuration = TimeDuration.fromCurrent( startTime );
                 final TimeDuration parseDuration = TimeDuration.fromCurrent( startTime );
                 LOGGER.trace( () -> "parsed PwmSettingXml in " + parseDuration.toString() + ", loads=" + LOAD_COUNTER.getAndIncrement() );
                 LOGGER.trace( () -> "parsed PwmSettingXml in " + parseDuration.toString() + ", loads=" + LOAD_COUNTER.getAndIncrement() );
 
 
@@ -102,15 +97,10 @@ public class PwmSettingXml
 
 
                 return newDoc;
                 return newDoc;
             }
             }
-            catch ( JDOMException e )
+            catch ( PwmUnrecoverableException e )
             {
             {
                 throw new IllegalStateException( "error parsing " + SETTING_XML_FILENAME + ": " + e.getMessage() );
                 throw new IllegalStateException( "error parsing " + SETTING_XML_FILENAME + ": " + e.getMessage() );
             }
             }
-            catch ( IOException e )
-            {
-                throw new IllegalStateException( "unable to load " + SETTING_XML_FILENAME + ": " + e.getMessage() );
-            }
-
         }
         }
         return docRefCopy;
         return docRefCopy;
     }
     }
@@ -132,28 +122,25 @@ public class PwmSettingXml
         }
         }
     }
     }
 
 
-    static Element readSettingXml( final PwmSetting setting )
+    static XmlElement readSettingXml( final PwmSetting setting )
     {
     {
-        final XPathFactory xpfac = XPathFactory.instance();
-        final XPathExpression xp = xpfac.compile( "/settings/setting[@key=\"" + setting.getKey() + "\"]" );
-        return ( Element ) xp.evaluateFirst( readXml() );
+        final String expression = "/settings/setting[@key=\"" + setting.getKey() + "\"]";
+        return readXml().evaluateXpathToElement( expression );
     }
     }
 
 
-    static Element readCategoryXml( final PwmSettingCategory category )
+    static XmlElement readCategoryXml( final PwmSettingCategory category )
     {
     {
-        final XPathFactory xpfac = XPathFactory.instance();
-        final XPathExpression xp = xpfac.compile( "/settings/category[@key=\"" + category.toString() + "\"]" );
-        return ( Element ) xp.evaluateFirst( readXml() );
+        final String expression = "/settings/category[@key=\"" + category.toString() + "\"]";
+        return readXml().evaluateXpathToElement( expression );
     }
     }
 
 
-    static Element readTemplateXml( final PwmSettingTemplate template )
+    static XmlElement readTemplateXml( final PwmSettingTemplate template )
     {
     {
-        final XPathFactory xpfac = XPathFactory.instance();
-        final XPathExpression xp = xpfac.compile( "/settings/template[@key=\"" + template.toString() + "\"]" );
-        return ( Element ) xp.evaluateFirst( readXml() );
+        final String expression = "/settings/template[@key=\"" + template.toString() + "\"]";
+        return readXml().evaluateXpathToElement( expression );
     }
     }
 
 
-    static Set<PwmSettingTemplate> parseTemplateAttribute( final Element element )
+    static Set<PwmSettingTemplate> parseTemplateAttribute( final XmlElement element )
     {
     {
         if ( element == null )
         if ( element == null )
         {
         {

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

@@ -22,9 +22,9 @@
 
 
 package password.pwm.config;
 package password.pwm.config;
 
 
-import org.jdom2.Element;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.util.java.XmlElement;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
@@ -33,7 +33,7 @@ import java.util.Locale;
 
 
 public interface StoredValue extends Serializable
 public interface StoredValue extends Serializable
 {
 {
-    List<Element> toXmlValues( String valueElementName, PwmSecurityKey pwmSecurityKey );
+    List<XmlElement> toXmlValues( String valueElementName, PwmSecurityKey pwmSecurityKey );
 
 
     Object toNativeObject( );
     Object toNativeObject( );
 
 
@@ -51,7 +51,7 @@ public interface StoredValue extends Serializable
     {
     {
         StoredValue fromJson( String input );
         StoredValue fromJson( String input );
 
 
-        StoredValue fromXmlElement( PwmSetting pwmSetting, Element settingElement, PwmSecurityKey key )
+        StoredValue fromXmlElement( PwmSetting pwmSetting, XmlElement settingElement, PwmSecurityKey key )
                 throws PwmException;
                 throws PwmException;
     }
     }
 
 

+ 77 - 119
server/src/main/java/password/pwm/config/stored/ConfigurationCleaner.java

@@ -22,23 +22,16 @@
 
 
 package password.pwm.config.stored;
 package password.pwm.config.stored;
 
 
-import org.jdom2.Comment;
-import org.jdom2.Document;
-import org.jdom2.Element;
-import org.jdom2.xpath.XPathExpression;
-import org.jdom2.xpath.XPathFactory;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.config.option.ADPolicyComplexity;
 import password.pwm.config.option.ADPolicyComplexity;
-import password.pwm.config.value.FormValue;
 import password.pwm.config.value.StringArrayValue;
 import password.pwm.config.value.StringArrayValue;
 import password.pwm.config.value.StringValue;
 import password.pwm.config.value.StringValue;
-import password.pwm.config.value.UserPermissionValue;
-import password.pwm.config.value.data.FormConfiguration;
-import password.pwm.config.value.data.UserPermission;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
+import password.pwm.util.java.XmlDocument;
+import password.pwm.util.java.XmlElement;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -50,33 +43,53 @@ class ConfigurationCleaner
     private static final PwmLogger LOGGER = PwmLogger.forClass( ConfigurationCleaner.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( ConfigurationCleaner.class );
     private static final String NEW_PROFILE_NAME = "default";
     private static final String NEW_PROFILE_NAME = "default";
 
 
-    static void cleanup( final StoredConfigurationImpl configuration, final Document document )
+    private final StoredConfigurationImpl storedConfiguration;
+    private final XmlDocument document;
+
+    ConfigurationCleaner(
+            final StoredConfigurationImpl storedConfiguration, final XmlDocument document
+    )
+    {
+        this.storedConfiguration = storedConfiguration;
+        this.document = document;
+    }
+
+
+    static void cleanup(
+            final StoredConfigurationImpl storedConfiguration, final XmlDocument document
+    )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
-        updateProperitiesWithoutType( configuration, document );
-        updateMandatoryElements( document );
-        profilizeNonProfiledSettings( configuration, document );
-        stripOrphanedProfileSettings( configuration, document );
-        migrateAppProperties( configuration, document );
-        updateDeprecatedSettings( configuration );
-        migrateDeprecatedProperties( configuration, document );
+        new ConfigurationCleaner( storedConfiguration, document ).cleanupImpl();
+    }
+
+    static void updateMandatoryElements(
+            final StoredConfigurationImpl storedConfiguration,
+            final XmlDocument document
+    )
+    {
+        new ConfigurationCleaner( storedConfiguration, document ).updateMandatoryElementsImpl();
     }
     }
 
 
 
 
-    static void updateMandatoryElements( final Document document )
+    private void cleanupImpl(
+    )
+            throws PwmUnrecoverableException
     {
     {
-        final Element rootElement = document.getRootElement();
+        updateProperitiesWithoutType( );
+        updateMandatoryElementsImpl();
+        profilizeNonProfiledSettings( );
+        stripOrphanedProfileSettings( );
+        migrateAppProperties( );
+        updateDeprecatedSettings( );
+        migrateDeprecatedProperties( );
+    }
 
 
-        {
-            final XPathExpression commentXPath = XPathFactory.instance().compile( "//comment()[1]" );
-            final Comment existingComment = ( Comment ) commentXPath.evaluateFirst( rootElement );
-            if ( existingComment != null )
-            {
-                existingComment.detach();
-            }
-            final Comment comment = new Comment( generateCommentText() );
-            rootElement.addContent( 0, comment );
-        }
+
+    private void updateMandatoryElementsImpl( )
+    {
+        final XmlElement rootElement = document.getRootElement();
+        rootElement.setComment( Collections.singletonList( generateCommentText() ) );
 
 
         rootElement.setAttribute( "pwmVersion", PwmConstants.BUILD_VERSION );
         rootElement.setAttribute( "pwmVersion", PwmConstants.BUILD_VERSION );
         rootElement.setAttribute( "pwmBuild", PwmConstants.BUILD_NUMBER );
         rootElement.setAttribute( "pwmBuild", PwmConstants.BUILD_NUMBER );
@@ -86,20 +99,18 @@ class ConfigurationCleaner
         {
         {
 
 
             // read correct (new) //properties[@type="config"]
             // read correct (new) //properties[@type="config"]
-            final XPathExpression configPropertiesXpath = XPathFactory.instance().compile(
-                    "//" + StoredConfiguration.XML_ELEMENT_PROPERTIES + "[@" + StoredConfiguration.XML_ATTRIBUTE_TYPE + "=\""
-                            + StoredConfiguration.XML_ATTRIBUTE_VALUE_CONFIG + "\"]" );
-            final Element configPropertiesElement = ( Element ) configPropertiesXpath.evaluateFirst( rootElement );
+            final String configPropertiesXpath = "//" + StoredConfigurationImpl.XML_ELEMENT_PROPERTIES
+                    + "[@" + StoredConfigurationImpl.XML_ATTRIBUTE_TYPE + "=\"" + StoredConfigurationImpl.XML_ATTRIBUTE_VALUE_CONFIG + "\"]";
+            final XmlElement configPropertiesElement = document.evaluateXpathToElement( configPropertiesXpath );
 
 
             // read list of old //properties[not (@type)]/property
             // read list of old //properties[not (@type)]/property
-            final XPathExpression nonAttributedProperty = XPathFactory.instance().compile(
-                    "//" + StoredConfiguration.XML_ELEMENT_PROPERTIES + "[not (@" + StoredConfiguration.XML_ATTRIBUTE_TYPE + ")]/"
-                            + StoredConfiguration.XML_ELEMENT_PROPERTY );
-            final List<Element> nonAttributedProperties = nonAttributedProperty.evaluate( rootElement );
+            final String nonAttributedPropertyXpath = "//" + StoredConfigurationImpl.XML_ELEMENT_PROPERTIES
+                    + "[not (@" + StoredConfigurationImpl.XML_ATTRIBUTE_TYPE + ")]/" + StoredConfigurationImpl.XML_ELEMENT_PROPERTY;
+            final List<XmlElement> nonAttributedProperties = document.evaluateXpathToElements( nonAttributedPropertyXpath );
 
 
             if ( configPropertiesElement != null && nonAttributedProperties != null )
             if ( configPropertiesElement != null && nonAttributedProperties != null )
             {
             {
-                for ( final Element element : nonAttributedProperties )
+                for ( final XmlElement element : nonAttributedProperties )
                 {
                 {
                     element.detach();
                     element.detach();
                     configPropertiesElement.addContent( element );
                     configPropertiesElement.addContent( element );
@@ -107,12 +118,11 @@ class ConfigurationCleaner
             }
             }
 
 
             // remove old //properties[not (@type] element
             // remove old //properties[not (@type] element
-            final XPathExpression oldPropertiesXpath = XPathFactory.instance().compile(
-                    "//" + StoredConfiguration.XML_ELEMENT_PROPERTIES + "[not (@" + StoredConfiguration.XML_ATTRIBUTE_TYPE + ")]" );
-            final List<Element> oldPropertiesElements = oldPropertiesXpath.evaluate( rootElement );
+            final String oldPropertiesXpath = "//" + StoredConfigurationImpl.XML_ELEMENT_PROPERTIES + "[not (@" + StoredConfigurationImpl.XML_ATTRIBUTE_TYPE + ")]";
+            final List<XmlElement> oldPropertiesElements = document.evaluateXpathToElements( oldPropertiesXpath );
             if ( oldPropertiesElements != null )
             if ( oldPropertiesElements != null )
             {
             {
-                for ( final Element element : oldPropertiesElements )
+                for ( final XmlElement element : oldPropertiesElements )
                 {
                 {
                     element.detach();
                     element.detach();
                 }
                 }
@@ -120,7 +130,7 @@ class ConfigurationCleaner
         }
         }
     }
     }
 
 
-    private static String generateCommentText( )
+    private String generateCommentText( )
     {
     {
         final StringBuilder commentText = new StringBuilder();
         final StringBuilder commentText = new StringBuilder();
         commentText.append( "\t\t" ).append( " " ).append( "\n" );
         commentText.append( "\t\t" ).append( " " ).append( "\n" );
@@ -146,15 +156,15 @@ class ConfigurationCleaner
     }
     }
 
 
 
 
-    private static void profilizeNonProfiledSettings( final StoredConfigurationImpl storedConfiguration, final Document document ) throws PwmUnrecoverableException
+    private void profilizeNonProfiledSettings()
+            throws PwmUnrecoverableException
     {
     {
         for ( final PwmSetting setting : PwmSetting.values() )
         for ( final PwmSetting setting : PwmSetting.values() )
         {
         {
             if ( setting.getCategory().hasProfiles() )
             if ( setting.getCategory().hasProfiles() )
             {
             {
 
 
-                final XPathExpression xp = StoredConfigurationImpl.XPathBuilder.xpathForSetting( setting, null );
-                final Element settingElement = ( Element ) xp.evaluateFirst( document );
+                final XmlElement settingElement = storedConfiguration.getXmlHelper().xpathForSetting( setting, null );
                 if ( settingElement != null )
                 if ( settingElement != null )
                 {
                 {
                     settingElement.detach();
                     settingElement.detach();
@@ -177,8 +187,8 @@ class ConfigurationCleaner
                         profileStringDefinitions.add( NEW_PROFILE_NAME );
                         profileStringDefinitions.add( NEW_PROFILE_NAME );
                     }
                     }
 
 
-                    final UserIdentity userIdentity = settingElement.getAttribute( StoredConfiguration.XML_ATTRIBUTE_MODIFY_USER ) != null
-                            ? UserIdentity.fromDelimitedKey( settingElement.getAttribute(  StoredConfiguration.XML_ATTRIBUTE_MODIFY_USER ).getValue() )
+                    final UserIdentity userIdentity = settingElement.getAttributeValue( StoredConfigurationImpl.XML_ATTRIBUTE_MODIFY_USER ) != null
+                            ? UserIdentity.fromDelimitedKey( settingElement.getAttributeValue(  StoredConfigurationImpl.XML_ATTRIBUTE_MODIFY_USER ) )
                             : null;
                             : null;
 
 
                     for ( final String destProfile : profileStringDefinitions )
                     for ( final String destProfile : profileStringDefinitions )
@@ -193,17 +203,13 @@ class ConfigurationCleaner
         }
         }
     }
     }
 
 
-    private static void migrateDeprecatedProperties(
-            final StoredConfigurationImpl storedConfiguration,
-            final Document document
+    private void migrateDeprecatedProperties(
     )
     )
             throws PwmUnrecoverableException
             throws PwmUnrecoverableException
     {
     {
-        final XPathFactory xpfac = XPathFactory.instance();
         {
         {
             final String xpathString = "//property[@key=\"" + ConfigurationProperty.LDAP_TEMPLATE.getKey() + "\"]";
             final String xpathString = "//property[@key=\"" + ConfigurationProperty.LDAP_TEMPLATE.getKey() + "\"]";
-            final XPathExpression xp = xpfac.compile( xpathString );
-            final List<Element> propertyElement = ( List<Element> ) xp.evaluate( document );
+            final List<XmlElement> propertyElement = document.evaluateXpathToElements( xpathString );
             if ( propertyElement != null && !propertyElement.isEmpty() )
             if ( propertyElement != null && !propertyElement.isEmpty() )
             {
             {
                 final String value = propertyElement.get( 0 ).getText();
                 final String value = propertyElement.get( 0 ).getText();
@@ -213,8 +219,7 @@ class ConfigurationCleaner
         }
         }
         {
         {
             final String xpathString = "//property[@key=\"" + ConfigurationProperty.NOTES.getKey() + "\"]";
             final String xpathString = "//property[@key=\"" + ConfigurationProperty.NOTES.getKey() + "\"]";
-            final XPathExpression xp = xpfac.compile( xpathString );
-            final List<Element> propertyElement = ( List<Element> ) xp.evaluate( document );
+            final List<XmlElement> propertyElement = document.evaluateXpathToElements( xpathString );
             if ( propertyElement != null && !propertyElement.isEmpty() )
             if ( propertyElement != null && !propertyElement.isEmpty() )
             {
             {
                 final String value = propertyElement.get( 0 ).getText();
                 final String value = propertyElement.get( 0 ).getText();
@@ -224,32 +229,28 @@ class ConfigurationCleaner
         }
         }
     }
     }
 
 
-    private static void updateProperitiesWithoutType( final StoredConfigurationImpl storedConfiguration, final Document document )
+    private void updateProperitiesWithoutType()
     {
     {
         final String xpathString = "//properties[not(@type)]";
         final String xpathString = "//properties[not(@type)]";
-        final XPathFactory xpfac = XPathFactory.instance();
-        final XPathExpression xp = xpfac.compile( xpathString );
-        final List<Element> propertiesElements = ( List<Element> ) xp.evaluate( document );
-        for ( final Element propertiesElement : propertiesElements )
+        final List<XmlElement> propertiesElements = document.evaluateXpathToElements( xpathString );
+        for ( final XmlElement propertiesElement : propertiesElements )
         {
         {
-            propertiesElement.setAttribute( StoredConfiguration.XML_ATTRIBUTE_TYPE, StoredConfiguration.XML_ATTRIBUTE_VALUE_CONFIG );
+            propertiesElement.setAttribute( StoredConfigurationImpl.XML_ATTRIBUTE_TYPE, StoredConfigurationImpl.XML_ATTRIBUTE_VALUE_CONFIG );
         }
         }
     }
     }
 
 
-    private static void stripOrphanedProfileSettings( final StoredConfigurationImpl storedConfiguration, final Document document )
+    private void stripOrphanedProfileSettings()
     {
     {
-        final XPathFactory xpfac = XPathFactory.instance();
         for ( final PwmSetting setting : PwmSetting.values() )
         for ( final PwmSetting setting : PwmSetting.values() )
         {
         {
             if ( setting.getCategory().hasProfiles() )
             if ( setting.getCategory().hasProfiles() )
             {
             {
                 final List<String> validProfiles = storedConfiguration.profilesForSetting( setting );
                 final List<String> validProfiles = storedConfiguration.profilesForSetting( setting );
                 final String xpathString = "//setting[@key=\"" + setting.getKey() + "\"]";
                 final String xpathString = "//setting[@key=\"" + setting.getKey() + "\"]";
-                final XPathExpression xp = xpfac.compile( xpathString );
-                final List<Element> settingElements = ( List<Element> ) xp.evaluate( document );
-                for ( final Element settingElement : settingElements )
+                final List<XmlElement> settingElements =  document.evaluateXpathToElements( xpathString );
+                for ( final XmlElement settingElement : settingElements )
                 {
                 {
-                    final String profileID = settingElement.getAttributeValue( StoredConfiguration.XML_ATTRIBUTE_PROFILE );
+                    final String profileID = settingElement.getAttributeValue( StoredConfigurationImpl.XML_ATTRIBUTE_PROFILE );
                     if ( profileID != null )
                     if ( profileID != null )
                     {
                     {
                         if ( !validProfiles.contains( profileID ) )
                         if ( !validProfiles.contains( profileID ) )
@@ -263,14 +264,15 @@ class ConfigurationCleaner
         }
         }
     }
     }
 
 
-    private static void migrateAppProperties( final StoredConfigurationImpl storedConfiguration, final Document document ) throws PwmUnrecoverableException
+    private void migrateAppProperties(
+    )
+            throws PwmUnrecoverableException
     {
     {
-        final XPathExpression xPathExpression = StoredConfigurationImpl.XPathBuilder.xpathForAppProperties();
-        final List<Element> appPropertiesElements = ( List<Element> ) xPathExpression.evaluate( document );
-        for ( final Element element : appPropertiesElements )
+        final List<XmlElement> appPropertiesElements = storedConfiguration.getXmlHelper().xpathForAppProperties();
+        for ( final XmlElement element : appPropertiesElements )
         {
         {
-            final List<Element> properties = element.getChildren();
-            for ( final Element property : properties )
+            final List<XmlElement> properties = element.getChildren();
+            for ( final XmlElement property : properties )
             {
             {
                 final String key = property.getAttributeValue( "key" );
                 final String key = property.getAttributeValue( "key" );
                 final String value = property.getText();
                 final String value = property.getText();
@@ -292,7 +294,7 @@ class ConfigurationCleaner
         }
         }
     }
     }
 
 
-    private static void updateDeprecatedSettings( final StoredConfigurationImpl storedConfiguration ) throws PwmUnrecoverableException
+    private void updateDeprecatedSettings( ) throws PwmUnrecoverableException
     {
     {
         final UserIdentity actor = new UserIdentity( "UpgradeProcessor", null );
         final UserIdentity actor = new UserIdentity( "UpgradeProcessor", null );
         for ( final String profileID : storedConfiguration.profilesForSetting( PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY ) )
         for ( final String profileID : storedConfiguration.profilesForSetting( PwmSetting.PASSWORD_POLICY_AD_COMPLEXITY ) )
@@ -336,49 +338,5 @@ class ConfigurationCleaner
                 storedConfiguration.resetSetting( PwmSetting.RECOVERY_ENFORCE_MINIMUM_PASSWORD_LIFETIME, profileID, actor );
                 storedConfiguration.resetSetting( PwmSetting.RECOVERY_ENFORCE_MINIMUM_PASSWORD_LIFETIME, profileID, actor );
             }
             }
         }
         }
-
-        {
-            if ( !storedConfiguration.isDefaultValue( PwmSetting.PEOPLE_SEARCH_SEARCH_ATTRIBUTES ) )
-            {
-                final List<String> oldValues = ( List<String> ) storedConfiguration.readSetting( PwmSetting.PEOPLE_SEARCH_SEARCH_ATTRIBUTES ).toNativeObject();
-
-                final List<FormConfiguration> newValues = new ArrayList<>();
-                for ( final String attribute : oldValues )
-                {
-                    final FormConfiguration formConfiguration = FormConfiguration.builder()
-                            .name( attribute )
-                            .labels( Collections.singletonMap( "", attribute ) )
-                            .build();
-                    newValues.add( formConfiguration );
-                }
-
-                final ValueMetaData existingData = storedConfiguration.readSettingMetadata( PwmSetting.PEOPLE_SEARCH_SEARCH_ATTRIBUTES, null );
-                final UserIdentity newActor = existingData != null && existingData.getUserIdentity() != null
-                        ? existingData.getUserIdentity()
-                        : actor;
-
-                storedConfiguration.writeSetting( PwmSetting.PEOPLE_SEARCH_SEARCH_FORM, null, new FormValue( newValues ), newActor );
-                storedConfiguration.resetSetting( PwmSetting.PEOPLE_SEARCH_SEARCH_ATTRIBUTES, null, actor );
-            }
-        }
-
-        {
-            if ( !storedConfiguration.isDefaultValue( PwmSetting.REPORTING_SEARCH_FILTER ) )
-            {
-                final String oldValue = ( String ) storedConfiguration.readSetting( PwmSetting.REPORTING_SEARCH_FILTER ).toNativeObject();
-
-                final List<UserPermission> newValues = new ArrayList<>();
-                final UserPermission newPermission = new UserPermission( UserPermission.Type.ldapQuery, PwmConstants.PROFILE_ID_ALL, oldValue, null );
-                newValues.add( newPermission );
-
-                final ValueMetaData existingData = storedConfiguration.readSettingMetadata( PwmSetting.REPORTING_SEARCH_FILTER, null );
-                final UserIdentity newActor = existingData != null && existingData.getUserIdentity() != null
-                        ? existingData.getUserIdentity()
-                        : actor;
-
-                storedConfiguration.writeSetting( PwmSetting.REPORTING_USER_MATCH, null, new UserPermissionValue( newValues ), newActor );
-                storedConfiguration.resetSetting( PwmSetting.REPORTING_SEARCH_FILTER, null, actor );
-            }
-        }
     }
     }
 }
 }

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

@@ -46,7 +46,6 @@ import java.math.BigInteger;
 import java.nio.file.Files;
 import java.nio.file.Files;
 import java.nio.file.StandardCopyOption;
 import java.nio.file.StandardCopyOption;
 import java.time.Instant;
 import java.time.Instant;
-import java.util.Date;
 import java.util.List;
 import java.util.List;
 
 
 /**
 /**
@@ -64,8 +63,6 @@ public class ConfigurationReader
     private StoredConfigurationImpl storedConfiguration;
     private StoredConfigurationImpl storedConfiguration;
     private ErrorInformation configFileError;
     private ErrorInformation configFileError;
 
 
-    private Date configurationReadTime;
-
     private PwmApplicationMode configMode = PwmApplicationMode.NEW;
     private PwmApplicationMode configMode = PwmApplicationMode.NEW;
 
 
     private volatile boolean saveInProgress;
     private volatile boolean saveInProgress;
@@ -124,8 +121,6 @@ public class ConfigurationReader
     {
     {
         LOGGER.debug( () -> "loading configuration file: " + configFile );
         LOGGER.debug( () -> "loading configuration file: " + configFile );
 
 
-        configurationReadTime = new Date();
-
         if ( !configFile.exists() )
         if ( !configFile.exists() )
         {
         {
             LOGGER.warn( "configuration file '" + configFile.getAbsolutePath() + "' does not exist" );
             LOGGER.warn( "configuration file '" + configFile.getAbsolutePath() + "' does not exist" );
@@ -165,6 +160,7 @@ public class ConfigurationReader
                     }
                     }
             );
             );
             this.configMode = PwmApplicationMode.ERROR;
             this.configMode = PwmApplicationMode.ERROR;
+            e.printStackTrace(  );
             throw new PwmUnrecoverableException( errorInformation );
             throw new PwmUnrecoverableException( errorInformation );
         }
         }
 
 
@@ -176,7 +172,7 @@ public class ConfigurationReader
                     {
                     {
                             errorMsg,
                             errorMsg,
                     }
                     }
-                    );
+            );
             this.configMode = PwmApplicationMode.ERROR;
             this.configMode = PwmApplicationMode.ERROR;
             throw new PwmUnrecoverableException( errorInformation );
             throw new PwmUnrecoverableException( errorInformation );
         }
         }

+ 27 - 19
server/src/main/java/password/pwm/config/stored/NGStoredConfigurationFactory.java

@@ -22,8 +22,6 @@
 
 
 package password.pwm.config.stored;
 package password.pwm.config.stored;
 
 
-import org.jdom2.Document;
-import org.jdom2.Element;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
@@ -31,7 +29,9 @@ import password.pwm.config.value.StringValue;
 import password.pwm.config.value.ValueFactory;
 import password.pwm.config.value.ValueFactory;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
-import password.pwm.util.java.XmlUtil;
+import password.pwm.util.java.XmlDocument;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
@@ -66,23 +66,23 @@ public class NGStoredConfigurationFactory
             final Map<StoredConfigReference, StoredValue> values = new LinkedHashMap<>();
             final Map<StoredConfigReference, StoredValue> values = new LinkedHashMap<>();
             final Map<StoredConfigReference, ValueMetaData> metaData = new LinkedHashMap<>();
             final Map<StoredConfigReference, ValueMetaData> metaData = new LinkedHashMap<>();
 
 
-            final Document inputDocument = XmlUtil.parseXml( inputStream );
-            final Element rootElement = inputDocument.getRootElement();
+            final XmlDocument inputDocument = XmlFactory.getFactory().parseXml( inputStream );
+            final XmlElement rootElement = inputDocument.getRootElement();
 
 
             final PwmSecurityKey pwmSecurityKey = readSecurityKey( rootElement );
             final PwmSecurityKey pwmSecurityKey = readSecurityKey( rootElement );
 
 
-            final Element settingsElement = rootElement.getChild( StoredConfiguration.XML_ELEMENT_SETTINGS );
+            final XmlElement settingsElement = rootElement.getChild( StoredConfiguration.XML_ELEMENT_SETTINGS );
 
 
-            for ( final Element loopElement : settingsElement.getChildren() )
+            for ( final XmlElement loopElement : settingsElement.getChildren() )
             {
             {
                 if ( StoredConfiguration.XML_ELEMENT_PROPERTIES.equals( loopElement.getName() ) )
                 if ( StoredConfiguration.XML_ELEMENT_PROPERTIES.equals( loopElement.getName() ) )
                 {
                 {
-                    for ( final Element propertyElement : loopElement.getChildren( StoredConfiguration.XML_ELEMENT_PROPERTY ) )
+                    for ( final XmlElement propertyElement : loopElement.getChildren( StoredConfiguration.XML_ELEMENT_PROPERTY ) )
                     {
                     {
                         readInterestingElement( propertyElement, pwmSecurityKey, values, metaData );
                         readInterestingElement( propertyElement, pwmSecurityKey, values, metaData );
                     }
                     }
                 }
                 }
-                else
+                else if ( StoredConfiguration.XML_ELEMENT_SETTING.equals( loopElement.getName() ) )
                 {
                 {
                     readInterestingElement( loopElement, pwmSecurityKey, values, metaData );
                     readInterestingElement( loopElement, pwmSecurityKey, values, metaData );
                 }
                 }
@@ -91,7 +91,7 @@ public class NGStoredConfigurationFactory
         }
         }
 
 
         static void readInterestingElement(
         static void readInterestingElement(
-                final Element loopElement,
+                final XmlElement loopElement,
                 final PwmSecurityKey pwmSecurityKey,
                 final PwmSecurityKey pwmSecurityKey,
                 final Map<StoredConfigReference, StoredValue> values,
                 final Map<StoredConfigReference, StoredValue> values,
                 final Map<StoredConfigReference, ValueMetaData> metaData
                 final Map<StoredConfigReference, ValueMetaData> metaData
@@ -105,7 +105,10 @@ public class NGStoredConfigurationFactory
                     case SETTING:
                     case SETTING:
                     {
                     {
                         final StoredValue storedValue = readSettingValue( reference, loopElement, pwmSecurityKey );
                         final StoredValue storedValue = readSettingValue( reference, loopElement, pwmSecurityKey );
-                        values.put( reference, storedValue );
+                        if ( storedValue != null )
+                        {
+                            values.put( reference, storedValue );
+                        }
                     }
                     }
                     break;
                     break;
 
 
@@ -126,7 +129,7 @@ public class NGStoredConfigurationFactory
             }
             }
         }
         }
 
 
-        static PwmSecurityKey readSecurityKey( final Element rootElement )
+        static PwmSecurityKey readSecurityKey( final XmlElement rootElement )
                 throws PwmUnrecoverableException
                 throws PwmUnrecoverableException
         {
         {
             final String createTime = rootElement.getAttributeValue( StoredConfiguration.XML_ATTRIBUTE_CREATE_TIME );
             final String createTime = rootElement.getAttributeValue( StoredConfiguration.XML_ATTRIBUTE_CREATE_TIME );
@@ -135,7 +138,7 @@ public class NGStoredConfigurationFactory
 
 
         static StoredValue readSettingValue(
         static StoredValue readSettingValue(
                 final StoredConfigReference storedConfigReference,
                 final StoredConfigReference storedConfigReference,
-                final Element settingElement,
+                final XmlElement settingElement,
                 final PwmSecurityKey pwmSecurityKey
                 final PwmSecurityKey pwmSecurityKey
         )
         )
         {
         {
@@ -149,7 +152,12 @@ public class NGStoredConfigurationFactory
             else
             else
             {
             {
                 LOGGER.trace( () -> "parsing setting key=" + key + ", profile=" + storedConfigReference.getProfileID() );
                 LOGGER.trace( () -> "parsing setting key=" + key + ", profile=" + storedConfigReference.getProfileID() );
-                if ( settingElement.getChild( StoredConfiguration.XML_ELEMENT_DEFAULT ) != null )
+                final XmlElement defaultElement = settingElement.getChild( StoredConfiguration.XML_ELEMENT_DEFAULT );
+                if ( defaultElement != null )
+                {
+                    return null;
+                }
+
                 {
                 {
                     try
                     try
                     {
                     {
@@ -166,7 +174,7 @@ public class NGStoredConfigurationFactory
 
 
         static StoredValue readPropertyValue(
         static StoredValue readPropertyValue(
                 final StoredConfigReference storedConfigReference,
                 final StoredConfigReference storedConfigReference,
-                final Element settingElement
+                final XmlElement settingElement
         )
         )
         {
         {
             final String key = storedConfigReference.getRecordID();
             final String key = storedConfigReference.getRecordID();
@@ -174,12 +182,12 @@ public class NGStoredConfigurationFactory
             LOGGER.trace( () -> "parsing property key=" + key + ", profile=" + storedConfigReference.getProfileID() );
             LOGGER.trace( () -> "parsing property key=" + key + ", profile=" + storedConfigReference.getProfileID() );
             if ( settingElement.getChild( StoredConfiguration.XML_ELEMENT_DEFAULT ) != null )
             if ( settingElement.getChild( StoredConfiguration.XML_ELEMENT_DEFAULT ) != null )
             {
             {
-                return new StringValue( settingElement.getValue() );
+                return new StringValue( settingElement.getText() );
             }
             }
             return null;
             return null;
         }
         }
 
 
-        static StoredConfigReference referenceForElement( final Element settingElement )
+        static StoredConfigReference referenceForElement( final XmlElement settingElement )
         {
         {
             final String key = settingElement.getAttributeValue( StoredConfiguration.XML_ATTRIBUTE_KEY );
             final String key = settingElement.getAttributeValue( StoredConfiguration.XML_ATTRIBUTE_KEY );
             final String profileID = readProfileID( settingElement );
             final String profileID = readProfileID( settingElement );
@@ -211,13 +219,13 @@ public class NGStoredConfigurationFactory
             );
             );
         }
         }
 
 
-        static String readProfileID( final Element settingElement )
+        static String readProfileID( final XmlElement settingElement )
         {
         {
             final String profileIDStr = settingElement.getAttributeValue( StoredConfiguration.XML_ATTRIBUTE_PROFILE );
             final String profileIDStr = settingElement.getAttributeValue( StoredConfiguration.XML_ATTRIBUTE_PROFILE );
             return profileIDStr != null && !profileIDStr.isEmpty() ? profileIDStr : null;
             return profileIDStr != null && !profileIDStr.isEmpty() ? profileIDStr : null;
         }
         }
 
 
-        static ValueMetaData readValueMetaData( final Element element )
+        static ValueMetaData readValueMetaData( final XmlElement element )
         {
         {
             final String modifyDateStr = element.getAttributeValue( StoredConfiguration.XML_ATTRIBUTE_MODIFY_TIME );
             final String modifyDateStr = element.getAttributeValue( StoredConfiguration.XML_ATTRIBUTE_MODIFY_TIME );
             Instant modifyDate = null;
             Instant modifyDate = null;

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

@@ -22,14 +22,6 @@
 
 
 package password.pwm.config.stored;
 package password.pwm.config.stored;
 
 
-import org.jdom2.Attribute;
-import org.jdom2.CDATA;
-import org.jdom2.Comment;
-import org.jdom2.Document;
-import org.jdom2.Element;
-import org.jdom2.Text;
-import org.jdom2.xpath.XPathExpression;
-import org.jdom2.xpath.XPathFactory;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
@@ -58,7 +50,9 @@ import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.TimeDuration;
 import password.pwm.util.java.TimeDuration;
-import password.pwm.util.java.XmlUtil;
+import password.pwm.util.java.XmlDocument;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.BCrypt;
 import password.pwm.util.secure.BCrypt;
 import password.pwm.util.secure.PwmRandom;
 import password.pwm.util.secure.PwmRandom;
@@ -71,6 +65,7 @@ import java.io.OutputStream;
 import java.io.Serializable;
 import java.io.Serializable;
 import java.time.Instant;
 import java.time.Instant;
 import java.util.ArrayList;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashMap;
@@ -101,13 +96,15 @@ public class StoredConfigurationImpl implements StoredConfiguration
     private static final PwmLogger LOGGER = PwmLogger.forClass( StoredConfigurationImpl.class );
     private static final PwmLogger LOGGER = PwmLogger.forClass( StoredConfigurationImpl.class );
     static final String XML_FORMAT_VERSION = "4";
     static final String XML_FORMAT_VERSION = "4";
 
 
-    private Document document = new Document( new Element( XML_ELEMENT_ROOT ) );
+    private XmlDocument document = XmlFactory.getFactory().newDocument( XML_ELEMENT_ROOT );
     private ChangeLog changeLog = new ChangeLog();
     private ChangeLog changeLog = new ChangeLog();
 
 
     private boolean locked;
     private boolean locked;
     private final boolean setting_writeLabels = true;
     private final boolean setting_writeLabels = true;
     private final ReentrantReadWriteLock domModifyLock = new ReentrantReadWriteLock();
     private final ReentrantReadWriteLock domModifyLock = new ReentrantReadWriteLock();
 
 
+    private final XmlHelper xmlHelper = new XmlHelper();
+
     public static StoredConfigurationImpl newStoredConfiguration( ) throws PwmUnrecoverableException
     public static StoredConfigurationImpl newStoredConfiguration( ) throws PwmUnrecoverableException
     {
     {
         return new StoredConfigurationImpl();
         return new StoredConfigurationImpl();
@@ -116,7 +113,7 @@ public class StoredConfigurationImpl implements StoredConfiguration
     public static StoredConfigurationImpl copy( final StoredConfigurationImpl input ) throws PwmUnrecoverableException
     public static StoredConfigurationImpl copy( final StoredConfigurationImpl input ) throws PwmUnrecoverableException
     {
     {
         final StoredConfigurationImpl copy = new StoredConfigurationImpl();
         final StoredConfigurationImpl copy = new StoredConfigurationImpl();
-        copy.document = input.document.clone();
+        copy.document = input.document.copy();
         return copy;
         return copy;
     }
     }
 
 
@@ -126,7 +123,7 @@ public class StoredConfigurationImpl implements StoredConfiguration
         final Instant startTime = Instant.now();
         final Instant startTime = Instant.now();
         //validateXmlSchema(xmlData);
         //validateXmlSchema(xmlData);
 
 
-        final Document inputDocument = XmlUtil.parseXml( xmlData );
+        final XmlDocument inputDocument = XmlFactory.getFactory().parseXml( xmlData );
         final StoredConfigurationImpl newConfiguration = StoredConfigurationImpl.newStoredConfiguration();
         final StoredConfigurationImpl newConfiguration = StoredConfigurationImpl.newStoredConfiguration();
 
 
         try
         try
@@ -197,11 +194,10 @@ public class StoredConfigurationImpl implements StoredConfiguration
                 this.resetSetting( settingValueRecord.getSetting(), settingValueRecord.getProfile(), null );
                 this.resetSetting( settingValueRecord.getSetting(), settingValueRecord.getProfile(), null );
                 if ( comment != null && !comment.isEmpty() )
                 if ( comment != null && !comment.isEmpty() )
                 {
                 {
-                    final XPathExpression xp = XPathBuilder.xpathForSetting( settingValueRecord.getSetting(), settingValueRecord.getProfile() );
-                    final Element settingElement = ( Element ) xp.evaluateFirst( document );
+                    final XmlElement settingElement = xmlHelper.xpathForSetting( settingValueRecord.getSetting(), settingValueRecord.getProfile() );
                     if ( settingElement != null )
                     if ( settingElement != null )
                     {
                     {
-                        settingElement.addContent( new Comment( comment ) );
+                        settingElement.setComment( Collections.singletonList( comment ) );
                     }
                     }
                 }
                 }
             }
             }
@@ -216,17 +212,24 @@ public class StoredConfigurationImpl implements StoredConfiguration
 
 
     public StoredConfigurationImpl( ) throws PwmUnrecoverableException
     public StoredConfigurationImpl( ) throws PwmUnrecoverableException
     {
     {
-        ConfigurationCleaner.cleanup( this, document );
-        final String createTime = JavaHelper.toIsoDate( Instant.now() );
-        document.getRootElement().setAttribute( XML_ATTRIBUTE_CREATE_TIME, createTime );
+        try
+        {
+            ConfigurationCleaner.cleanup( this, document );
+            final String createTime = JavaHelper.toIsoDate( Instant.now() );
+            document.getRootElement().setAttribute( XML_ATTRIBUTE_CREATE_TIME, createTime );
+        }
+        catch ( Exception e )
+        {
+            e.printStackTrace(  );
+            throw new IllegalStateException( e );
+        }
     }
     }
 
 
 
 
     @Override
     @Override
     public String readConfigProperty( final ConfigurationProperty propertyName )
     public String readConfigProperty( final ConfigurationProperty propertyName )
     {
     {
-        final XPathExpression xp = XPathBuilder.xpathForConfigProperty( propertyName );
-        final Element propertyElement = ( Element ) xp.evaluateFirst( document );
+        final XmlElement propertyElement = xmlHelper.xpathForConfigProperty( propertyName );
         return propertyElement == null ? null : propertyElement.getText();
         return propertyElement == null ? null : propertyElement.getText();
     }
     }
 
 
@@ -240,26 +243,28 @@ public class StoredConfigurationImpl implements StoredConfiguration
         try
         try
         {
         {
 
 
-            final XPathExpression xp = XPathBuilder.xpathForConfigProperty( propertyName );
-            final List<Element> propertyElements = xp.evaluate( document );
-            for ( final Element propertyElement : propertyElements )
+            // remove existing element
             {
             {
+                final XmlElement propertyElement  = xmlHelper.xpathForConfigProperty( propertyName );
                 propertyElement.detach();
                 propertyElement.detach();
             }
             }
 
 
-            final Element propertyElement = new Element( XML_ELEMENT_PROPERTY );
-            propertyElement.setAttribute( new Attribute( XML_ATTRIBUTE_KEY, propertyName.getKey() ) );
-            propertyElement.setContent( new Text( value ) );
+            // add new property
+            {
+
+            }
+            final XmlElement propertyElement = xmlHelper.getXmlFactory().newElement( XML_ELEMENT_PROPERTY );
+            propertyElement.setAttribute( XML_ATTRIBUTE_KEY, propertyName.getKey() );
+            propertyElement.addText( value );
 
 
-            if ( null == XPathBuilder.xpathForConfigProperties().evaluateFirst( document ) )
+            if ( null == xmlHelper.xpathForConfigProperties() )
             {
             {
-                final Element configProperties = new Element( XML_ELEMENT_PROPERTIES );
-                configProperties.setAttribute( new Attribute( XML_ATTRIBUTE_TYPE, XML_ATTRIBUTE_VALUE_CONFIG ) );
+                final XmlElement configProperties = xmlHelper.getXmlFactory().newElement( XML_ELEMENT_PROPERTIES );
+                configProperties.setAttribute( XML_ATTRIBUTE_TYPE, XML_ATTRIBUTE_VALUE_CONFIG );
                 document.getRootElement().addContent( configProperties );
                 document.getRootElement().addContent( configProperties );
             }
             }
 
 
-            final XPathExpression xp2 = XPathBuilder.xpathForConfigProperties();
-            final Element propertiesElement = ( Element ) xp2.evaluateFirst( document );
+            final XmlElement propertiesElement = xmlHelper.xpathForConfigProperties();
             propertyElement.setAttribute( XML_ATTRIBUTE_MODIFY_TIME, JavaHelper.toIsoDate( Instant.now() ) );
             propertyElement.setAttribute( XML_ATTRIBUTE_MODIFY_TIME, JavaHelper.toIsoDate( Instant.now() ) );
             propertiesElement.setAttribute( XML_ATTRIBUTE_MODIFY_TIME, JavaHelper.toIsoDate( Instant.now() ) );
             propertiesElement.setAttribute( XML_ATTRIBUTE_MODIFY_TIME, JavaHelper.toIsoDate( Instant.now() ) );
             propertiesElement.addContent( propertyElement );
             propertiesElement.addContent( propertyElement );
@@ -280,12 +285,12 @@ public class StoredConfigurationImpl implements StoredConfiguration
         domModifyLock.readLock().lock();
         domModifyLock.readLock().lock();
         try
         try
         {
         {
-            final XPathExpression xp = XPathBuilder.xpathForLocaleBundleSetting( bundleName, keyName );
-            final Element localeBundleElement = ( Element ) xp.evaluateFirst( document );
+            final XmlElement localeBundleElement = xmlHelper.xpathForLocaleBundleSetting( bundleName, keyName );
+
             if ( localeBundleElement != null )
             if ( localeBundleElement != null )
             {
             {
                 final Map<String, String> bundleMap = new LinkedHashMap<>();
                 final Map<String, String> bundleMap = new LinkedHashMap<>();
-                for ( final Element valueElement : localeBundleElement.getChildren( "value" ) )
+                for ( final XmlElement valueElement : localeBundleElement.getChildren( "value" ) )
                 {
                 {
                     final String localeStrValue = valueElement.getAttributeValue( "locale" );
                     final String localeStrValue = valueElement.getAttributeValue( "locale" );
                     bundleMap.put( localeStrValue == null ? "" : localeStrValue, valueElement.getText() );
                     bundleMap.put( localeStrValue == null ? "" : localeStrValue, valueElement.getText() );
@@ -346,14 +351,10 @@ public class StoredConfigurationImpl implements StoredConfiguration
         domModifyLock.writeLock().lock();
         domModifyLock.writeLock().lock();
         try
         try
         {
         {
-            final XPathExpression xp = XPathBuilder.xpathForLocaleBundleSetting( bundleName, keyName );
-            final List<Element> oldBundleElements = xp.evaluate( document );
+            final XmlElement oldBundleElements = xmlHelper.xpathForLocaleBundleSetting( bundleName, keyName );
             if ( oldBundleElements != null )
             if ( oldBundleElements != null )
             {
             {
-                for ( final Element element : oldBundleElements )
-                {
-                    element.detach();
-                }
+                oldBundleElements.detach();
             }
             }
         }
         }
         finally
         finally
@@ -369,9 +370,9 @@ public class StoredConfigurationImpl implements StoredConfiguration
         try
         try
         {
         {
             preModifyActions();
             preModifyActions();
-            final Element settingElement = createOrGetSettingElement( document, setting, profileID );
+            final XmlElement settingElement = createOrGetSettingElement( setting, profileID );
             settingElement.removeContent();
             settingElement.removeContent();
-            settingElement.addContent( new Element( XML_ELEMENT_DEFAULT ) );
+            settingElement.addContent( xmlHelper.getXmlFactory().newElement( XML_ELEMENT_DEFAULT ) );
             updateMetaData( settingElement, userIdentity );
             updateMetaData( settingElement, userIdentity );
         }
         }
         finally
         finally
@@ -414,16 +415,15 @@ public class StoredConfigurationImpl implements StoredConfiguration
     public PwmSettingTemplateSet getTemplateSet( )
     public PwmSettingTemplateSet getTemplateSet( )
     {
     {
         final Set<PwmSettingTemplate> templates = new HashSet<>();
         final Set<PwmSettingTemplate> templates = new HashSet<>();
-        templates.add( readTemplateValue( document, PwmSetting.TEMPLATE_LDAP ) );
-        templates.add( readTemplateValue( document, PwmSetting.TEMPLATE_STORAGE ) );
-        templates.add( readTemplateValue( document, PwmSetting.DB_VENDOR_TEMPLATE ) );
+        templates.add( readTemplateValue( PwmSetting.TEMPLATE_LDAP ) );
+        templates.add( readTemplateValue( PwmSetting.TEMPLATE_STORAGE ) );
+        templates.add( readTemplateValue( PwmSetting.DB_VENDOR_TEMPLATE ) );
         return new PwmSettingTemplateSet( templates );
         return new PwmSettingTemplateSet( templates );
     }
     }
 
 
-    private static PwmSettingTemplate readTemplateValue( final Document document, final PwmSetting pwmSetting )
+    private PwmSettingTemplate readTemplateValue( final PwmSetting pwmSetting )
     {
     {
-        final XPathExpression xp = XPathBuilder.xpathForSetting( pwmSetting, null );
-        final Element settingElement = ( Element ) xp.evaluateFirst( document );
+        final XmlElement settingElement = xmlHelper.xpathForSetting( pwmSetting, null );
         if ( settingElement != null )
         if ( settingElement != null )
         {
         {
             try
             try
@@ -565,8 +565,8 @@ public class StoredConfigurationImpl implements StoredConfiguration
     public void toXml( final OutputStream outputStream )
     public void toXml( final OutputStream outputStream )
             throws IOException, PwmUnrecoverableException
             throws IOException, PwmUnrecoverableException
     {
     {
-        ConfigurationCleaner.updateMandatoryElements( document );
-        XmlUtil.outputDocument( document, outputStream );
+        ConfigurationCleaner.updateMandatoryElements( this, document );
+        XmlFactory.getFactory().outputDocument( document, outputStream );
     }
     }
 
 
     public List<String> profilesForSetting( final PwmSetting pwmSetting )
     public List<String> profilesForSetting( final PwmSetting pwmSetting )
@@ -627,8 +627,7 @@ public class StoredConfigurationImpl implements StoredConfiguration
 
 
     public ValueMetaData readSettingMetadata( final PwmSetting setting, final String profileID )
     public ValueMetaData readSettingMetadata( final PwmSetting setting, final String profileID )
     {
     {
-        final XPathExpression xp = XPathBuilder.xpathForSetting( setting, profileID );
-        final Element settingElement = ( Element ) xp.evaluateFirst( document );
+        final XmlElement settingElement = xmlHelper.xpathForSetting( setting, profileID );
 
 
         if ( settingElement == null )
         if ( settingElement == null )
         {
         {
@@ -755,7 +754,7 @@ public class StoredConfigurationImpl implements StoredConfiguration
         if ( PwmSettingSyntax.SELECT == setting.getSyntax()
         if ( PwmSettingSyntax.SELECT == setting.getSyntax()
                 || PwmSettingSyntax.OPTIONLIST == setting.getSyntax()
                 || PwmSettingSyntax.OPTIONLIST == setting.getSyntax()
                 || PwmSettingSyntax.VERIFICATION_METHOD == setting.getSyntax()
                 || PwmSettingSyntax.VERIFICATION_METHOD == setting.getSyntax()
-                )
+        )
         {
         {
             for ( final String key : setting.getOptions().keySet() )
             for ( final String key : setting.getOptions().keySet() )
             {
             {
@@ -794,8 +793,7 @@ public class StoredConfigurationImpl implements StoredConfiguration
         domModifyLock.readLock().lock();
         domModifyLock.readLock().lock();
         try
         try
         {
         {
-            final XPathExpression xp = XPathBuilder.xpathForSetting( setting, profileID );
-            final Element settingElement = ( Element ) xp.evaluateFirst( document );
+            final XmlElement settingElement = xmlHelper.xpathForSetting( setting, profileID );
 
 
             if ( settingElement == null )
             if ( settingElement == null )
             {
             {
@@ -859,19 +857,19 @@ public class StoredConfigurationImpl implements StoredConfiguration
         try
         try
         {
         {
             domModifyLock.writeLock().lock();
             domModifyLock.writeLock().lock();
-            final Element localeBundleElement = new Element( "localeBundle" );
+            final XmlElement localeBundleElement = xmlHelper.getXmlFactory().newElement( "localeBundle" );
             localeBundleElement.setAttribute( "bundle", bundleName );
             localeBundleElement.setAttribute( "bundle", bundleName );
             localeBundleElement.setAttribute( "key", keyName );
             localeBundleElement.setAttribute( "key", keyName );
             for ( final Map.Entry<String, String> entry : localeMap.entrySet() )
             for ( final Map.Entry<String, String> entry : localeMap.entrySet() )
             {
             {
                 final String locale = entry.getKey();
                 final String locale = entry.getKey();
                 final String value = entry.getValue();
                 final String value = entry.getValue();
-                final Element valueElement = new Element( "value" );
+                final XmlElement valueElement = xmlHelper.getXmlFactory().newElement( "value" );
                 if ( locale != null && locale.length() > 0 )
                 if ( locale != null && locale.length() > 0 )
                 {
                 {
                     valueElement.setAttribute( "locale", locale );
                     valueElement.setAttribute( "locale", locale );
                 }
                 }
-                valueElement.setContent( new CDATA( value ) );
+                valueElement.addText( value );
                 localeBundleElement.addContent( valueElement );
                 localeBundleElement.addContent( valueElement );
             }
             }
             localeBundleElement.setAttribute( XML_ATTRIBUTE_MODIFY_TIME, JavaHelper.toIsoDate( Instant.now() ) );
             localeBundleElement.setAttribute( XML_ATTRIBUTE_MODIFY_TIME, JavaHelper.toIsoDate( Instant.now() ) );
@@ -954,35 +952,47 @@ public class StoredConfigurationImpl implements StoredConfiguration
         domModifyLock.writeLock().lock();
         domModifyLock.writeLock().lock();
         try
         try
         {
         {
-            final Element settingElement = createOrGetSettingElement( document, setting, profileID );
+            final XmlElement settingElement = createOrGetSettingElement( setting, profileID );
             settingElement.removeContent();
             settingElement.removeContent();
             settingElement.setAttribute( XML_ATTRIBUTE_SYNTAX, setting.getSyntax().toString() );
             settingElement.setAttribute( XML_ATTRIBUTE_SYNTAX, setting.getSyntax().toString() );
             settingElement.setAttribute( XML_ATTRIBUTE_SYNTAX_VERSION, Integer.toString( value.currentSyntaxVersion() ) );
             settingElement.setAttribute( XML_ATTRIBUTE_SYNTAX_VERSION, Integer.toString( value.currentSyntaxVersion() ) );
 
 
             if ( setting_writeLabels )
             if ( setting_writeLabels )
             {
             {
-                final Element labelElement = new Element( "label" );
-                labelElement.addContent( setting.getLabel( PwmConstants.DEFAULT_LOCALE ) );
-                settingElement.addContent( labelElement );
+                {
+                    final XmlElement existingLabel = settingElement.getChild( "label" );
+                    if ( existingLabel != null )
+                    {
+                        existingLabel.detach();
+                    }
+                }
+
+                {
+                    final XmlElement newLabelElement = xmlHelper.getXmlFactory().newElement( "label" );
+                    newLabelElement.addText( setting.getLabel( PwmConstants.DEFAULT_LOCALE ) );
+                    settingElement.addContent( newLabelElement );
+                }
             }
             }
 
 
             if ( setting.getSyntax() == PwmSettingSyntax.PASSWORD )
             if ( setting.getSyntax() == PwmSettingSyntax.PASSWORD )
             {
             {
-                final List<Element> valueElements = ( ( PasswordValue ) value ).toXmlValues( "value", getKey() );
-                settingElement.addContent( new Comment( "Note: This value is encrypted and can not be edited directly." ) );
-                settingElement.addContent( new Comment( "Please use the Configuration Manager web UI to modify this value or add" ) );
-                settingElement.addContent( new Comment( "plaintext=\"true\" attribute on <value> tag to use a non-encrypted value." ) );
+                final List<String> commentLines = Arrays.asList(
+                        "Note: This value is encrypted and can not be edited directly.",
+                        "Please use the Configuration Manager GUI to modify this value."
+                );
+                settingElement.setComment( commentLines );
 
 
+                final List<XmlElement> valueElements = ( ( PasswordValue ) value ).toXmlValues( "value", getKey() );
                 settingElement.addContent( valueElements );
                 settingElement.addContent( valueElements );
             }
             }
             else if ( setting.getSyntax() == PwmSettingSyntax.PRIVATE_KEY )
             else if ( setting.getSyntax() == PwmSettingSyntax.PRIVATE_KEY )
             {
             {
-                final List<Element> valueElements = ( ( PrivateKeyValue ) value ).toXmlValues( "value", getKey() );
+                final List<XmlElement> valueElements = ( ( PrivateKeyValue ) value ).toXmlValues( "value", getKey() );
                 settingElement.addContent( valueElements );
                 settingElement.addContent( valueElements );
             }
             }
             else if ( setting.getSyntax() == PwmSettingSyntax.NAMED_SECRET )
             else if ( setting.getSyntax() == PwmSettingSyntax.NAMED_SECRET )
             {
             {
-                final List<Element> valueElements = ( ( NamedSecretValue ) value ).toXmlValues( "value", getKey() );
+                final List<XmlElement> valueElements = ( ( NamedSecretValue ) value ).toXmlValues( "value", getKey() );
                 settingElement.addContent( valueElements );
                 settingElement.addContent( valueElements );
             }
             }
             else
             else
@@ -1063,19 +1073,21 @@ public class StoredConfigurationImpl implements StoredConfiguration
         return passwordHash != null && passwordHash.length() > 0;
         return passwordHash != null && passwordHash.length() > 0;
     }
     }
 
 
-    abstract static class XPathBuilder
+    class XmlHelper
     {
     {
-        private static XPathExpression xpathForLocaleBundleSetting( final String bundleName, final String keyName )
+        private XmlFactory getXmlFactory()
         {
         {
-            final XPathFactory xpfac = XPathFactory.instance();
-            final String xpathString;
-            xpathString = "//localeBundle[@bundle=\"" + bundleName + "\"][@key=\"" + keyName + "\"]";
-            return xpfac.compile( xpathString );
+            return XmlFactory.getFactory();
         }
         }
 
 
-        static XPathExpression xpathForSetting( final PwmSetting setting, final String profileID )
+        private XmlElement xpathForLocaleBundleSetting( final String bundleName, final String keyName )
+        {
+            final String xpathString = "//localeBundle[@bundle=\"" + bundleName + "\"][@key=\"" + keyName + "\"]";
+            return document.evaluateXpathToElement( xpathString );
+        }
+
+        XmlElement xpathForSetting( final PwmSetting setting, final String profileID )
         {
         {
-            final XPathFactory xpfac = XPathFactory.instance();
             final String xpathString;
             final String xpathString;
             if ( profileID == null || profileID.length() < 1 )
             if ( profileID == null || profileID.length() < 1 )
             {
             {
@@ -1086,41 +1098,33 @@ public class StoredConfigurationImpl implements StoredConfiguration
                 xpathString = "//setting[@key=\"" + setting.getKey() + "\"][@profile=\"" + profileID + "\"]";
                 xpathString = "//setting[@key=\"" + setting.getKey() + "\"][@profile=\"" + profileID + "\"]";
             }
             }
 
 
-            return xpfac.compile( xpathString );
+            return document.evaluateXpathToElement( xpathString );
         }
         }
 
 
-        private static XPathExpression xpathForAppProperty( final AppProperty appProperty )
+        private XmlElement xpathForAppProperty( final AppProperty appProperty )
         {
         {
-            final XPathFactory xpfac = XPathFactory.instance();
-            final String xpathString;
-            xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_APP + "\"]/"
+            final String xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_APP + "\"]/"
                     + XML_ELEMENT_PROPERTY + "[@" + XML_ATTRIBUTE_KEY + "=\"" + appProperty.getKey() + "\"]";
                     + XML_ELEMENT_PROPERTY + "[@" + XML_ATTRIBUTE_KEY + "=\"" + appProperty.getKey() + "\"]";
-            return xpfac.compile( xpathString );
+            return document.evaluateXpathToElement( xpathString );
         }
         }
 
 
-        static XPathExpression xpathForAppProperties()
+        List<XmlElement> xpathForAppProperties( )
         {
         {
-            final XPathFactory xpfac = XPathFactory.instance();
-            final String xpathString;
-            xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_APP + "\"]";
-            return xpfac.compile( xpathString );
+            final String xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_APP + "\"]";
+            return document.evaluateXpathToElements( xpathString );
         }
         }
 
 
-        private static XPathExpression xpathForConfigProperty( final ConfigurationProperty configProperty )
+        private XmlElement xpathForConfigProperty( final ConfigurationProperty configProperty )
         {
         {
-            final XPathFactory xpfac = XPathFactory.instance();
-            final String xpathString;
-            xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_CONFIG + "\"]/"
+            final String xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_CONFIG + "\"]/"
                     + XML_ELEMENT_PROPERTY + "[@" + XML_ATTRIBUTE_KEY + "=\"" + configProperty.getKey() + "\"]";
                     + XML_ELEMENT_PROPERTY + "[@" + XML_ATTRIBUTE_KEY + "=\"" + configProperty.getKey() + "\"]";
-            return xpfac.compile( xpathString );
+            return document.evaluateXpathToElement( xpathString );
         }
         }
 
 
-        private static XPathExpression xpathForConfigProperties( )
+        private XmlElement xpathForConfigProperties( )
         {
         {
-            final XPathFactory xpfac = XPathFactory.instance();
-            final String xpathString;
-            xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_CONFIG + "\"]";
-            return xpfac.compile( xpathString );
+            final String xpathString = "//" + XML_ELEMENT_PROPERTIES + "[@" + XML_ATTRIBUTE_TYPE + "=\"" + XML_ATTRIBUTE_VALUE_CONFIG + "\"]";
+            return document.evaluateXpathToElement( xpathString );
         }
         }
     }
     }
 
 
@@ -1354,9 +1358,9 @@ public class StoredConfigurationImpl implements StoredConfiguration
         */
         */
     }
     }
 
 
-    private static void updateMetaData( final Element settingElement, final UserIdentity userIdentity )
+    private void updateMetaData( final XmlElement settingElement, final UserIdentity userIdentity )
     {
     {
-        final Element settingsElement = settingElement.getDocument().getRootElement().getChild( XML_ELEMENT_SETTINGS );
+        final XmlElement settingsElement = document.getRootElement().getChild( XML_ELEMENT_SETTINGS );
         settingElement.setAttribute( XML_ATTRIBUTE_MODIFY_TIME, JavaHelper.toIsoDate( Instant.now() ) );
         settingElement.setAttribute( XML_ATTRIBUTE_MODIFY_TIME, JavaHelper.toIsoDate( Instant.now() ) );
         settingsElement.setAttribute( XML_ATTRIBUTE_MODIFY_TIME, JavaHelper.toIsoDate( Instant.now() ) );
         settingsElement.setAttribute( XML_ATTRIBUTE_MODIFY_TIME, JavaHelper.toIsoDate( Instant.now() ) );
         settingElement.removeAttribute( XML_ATTRIBUTE_MODIFY_USER );
         settingElement.removeAttribute( XML_ATTRIBUTE_MODIFY_USER );
@@ -1368,20 +1372,18 @@ public class StoredConfigurationImpl implements StoredConfiguration
         }
         }
     }
     }
 
 
-    private static Element createOrGetSettingElement(
-            final Document document,
+    private XmlElement createOrGetSettingElement(
             final PwmSetting setting,
             final PwmSetting setting,
             final String profileID
             final String profileID
     )
     )
     {
     {
-        final XPathExpression xp = XPathBuilder.xpathForSetting( setting, profileID );
-        final Element existingSettingElement = ( Element ) xp.evaluateFirst( document );
+        final XmlElement existingSettingElement = xmlHelper.xpathForSetting( setting, profileID );
         if ( existingSettingElement != null )
         if ( existingSettingElement != null )
         {
         {
             return existingSettingElement;
             return existingSettingElement;
         }
         }
 
 
-        final Element settingElement = new Element( XML_ELEMENT_SETTING );
+        final XmlElement settingElement = xmlHelper.getXmlFactory().newElement( XML_ELEMENT_SETTING );
         settingElement.setAttribute( XML_ATTRIBUTE_KEY, setting.getKey() );
         settingElement.setAttribute( XML_ATTRIBUTE_KEY, setting.getKey() );
         settingElement.setAttribute( XML_ATTRIBUTE_SYNTAX, setting.getSyntax().toString() );
         settingElement.setAttribute( XML_ATTRIBUTE_SYNTAX, setting.getSyntax().toString() );
         if ( profileID != null && profileID.length() > 0 )
         if ( profileID != null && profileID.length() > 0 )
@@ -1389,11 +1391,11 @@ public class StoredConfigurationImpl implements StoredConfiguration
             settingElement.setAttribute( XML_ATTRIBUTE_PROFILE, profileID );
             settingElement.setAttribute( XML_ATTRIBUTE_PROFILE, profileID );
         }
         }
 
 
-        Element settingsElement = document.getRootElement().getChild( XML_ELEMENT_SETTINGS );
+        XmlElement settingsElement = document.getRootElement().getChild( XML_ELEMENT_SETTINGS );
         if ( settingsElement == null )
         if ( settingsElement == null )
         {
         {
-            settingsElement = new Element( XML_ELEMENT_SETTINGS );
-            document.getRootElement().addContent( settingsElement );
+            settingsElement = xmlHelper.getXmlFactory().newElement( XML_ELEMENT_SETTINGS );
+            document.getRootElement().addContent( settingElement );
         }
         }
         settingsElement.addContent( settingElement );
         settingsElement.addContent( settingElement );
 
 
@@ -1498,7 +1500,7 @@ public class StoredConfigurationImpl implements StoredConfiguration
 
 
     private String createTime( )
     private String createTime( )
     {
     {
-        final Element rootElement = document.getRootElement();
+        final XmlElement rootElement = document.getRootElement();
         final String createTimeString = rootElement.getAttributeValue( XML_ATTRIBUTE_CREATE_TIME );
         final String createTimeString = rootElement.getAttributeValue( XML_ATTRIBUTE_CREATE_TIME );
         if ( createTimeString == null || createTimeString.isEmpty() )
         if ( createTimeString == null || createTimeString.isEmpty() )
         {
         {
@@ -1510,7 +1512,7 @@ public class StoredConfigurationImpl implements StoredConfiguration
     @Override
     @Override
     public Instant modifyTime( )
     public Instant modifyTime( )
     {
     {
-        final Element rootElement = document.getRootElement();
+        final XmlElement rootElement = document.getRootElement();
         final String modifyTimeString = rootElement.getAttributeValue( XML_ATTRIBUTE_MODIFY_TIME );
         final String modifyTimeString = rootElement.getAttributeValue( XML_ATTRIBUTE_MODIFY_TIME );
         if ( modifyTimeString != null )
         if ( modifyTimeString != null )
         {
         {
@@ -1570,4 +1572,8 @@ public class StoredConfigurationImpl implements StoredConfiguration
         return new ArrayList<>( loopResults );
         return new ArrayList<>( loopResults );
     }
     }
 
 
+    XmlHelper getXmlHelper()
+    {
+        return xmlHelper;
+    }
 }
 }

+ 3 - 11
server/src/main/java/password/pwm/config/stored/StoredConfigurationUtil.java

@@ -26,11 +26,11 @@ import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSettingCategory;
 import password.pwm.config.PwmSettingCategory;
 import password.pwm.config.PwmSettingSyntax;
 import password.pwm.config.PwmSettingSyntax;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
+import password.pwm.util.java.StringUtil;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Collections;
-import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.List;
 import java.util.TreeMap;
 import java.util.TreeMap;
@@ -92,16 +92,8 @@ public abstract class StoredConfigurationUtil
 
 
         final Object nativeObject = storedConfiguration.readSetting( profileSetting ).toNativeObject();
         final Object nativeObject = storedConfiguration.readSetting( profileSetting ).toNativeObject();
         final List<String> settingValues = ( List<String> ) nativeObject;
         final List<String> settingValues = ( List<String> ) nativeObject;
-        final LinkedList<String> profiles = new LinkedList<>();
-        profiles.addAll( settingValues );
-        for ( final Iterator<String> iterator = profiles.iterator(); iterator.hasNext(); )
-        {
-            final String profile = iterator.next();
-            if ( profile == null || profile.length() < 1 )
-            {
-                iterator.remove();
-            }
-        }
+        final LinkedList<String> profiles = new LinkedList<>( settingValues );
+        profiles.removeIf( profile -> StringUtil.isEmpty( profile ) );
         return Collections.unmodifiableList( profiles );
         return Collections.unmodifiableList( profiles );
 
 
     }
     }

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

@@ -22,29 +22,15 @@
 
 
 package password.pwm.config.stored;
 package password.pwm.config.stored;
 
 
+import lombok.Value;
 import password.pwm.bean.UserIdentity;
 import password.pwm.bean.UserIdentity;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
 import java.time.Instant;
 import java.time.Instant;
 
 
+@Value
 public class ValueMetaData implements Serializable
 public class ValueMetaData implements Serializable
 {
 {
     private Instant modifyDate;
     private Instant modifyDate;
     private UserIdentity userIdentity;
     private UserIdentity userIdentity;
-
-    public ValueMetaData( final Instant modifyDate, final UserIdentity userIdentity )
-    {
-        this.modifyDate = modifyDate;
-        this.userIdentity = userIdentity;
-    }
-
-    public Instant getModifyDate( )
-    {
-        return modifyDate;
-    }
-
-    public UserIdentity getUserIdentity( )
-    {
-        return userIdentity;
-    }
 }
 }

+ 11 - 10
server/src/main/java/password/pwm/config/value/ActionValue.java

@@ -23,7 +23,6 @@
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
-import org.jdom2.Element;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSettingSyntax;
 import password.pwm.config.PwmSettingSyntax;
@@ -35,6 +34,8 @@ import password.pwm.error.PwmOperationalException;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.X509Utils;
 import password.pwm.util.secure.X509Utils;
@@ -90,7 +91,7 @@ public class ActionValue extends AbstractValue implements StoredValue
 
 
             public ActionValue fromXmlElement(
             public ActionValue fromXmlElement(
                     final PwmSetting pwmSetting,
                     final PwmSetting pwmSetting,
-                    final Element settingElement,
+                    final XmlElement settingElement,
                     final PwmSecurityKey pwmSecurityKey
                     final PwmSecurityKey pwmSecurityKey
             )
             )
                     throws PwmOperationalException
                     throws PwmOperationalException
@@ -100,8 +101,8 @@ public class ActionValue extends AbstractValue implements StoredValue
 
 
                 final boolean oldType = PwmSettingSyntax.STRING_ARRAY.toString().equals(
                 final boolean oldType = PwmSettingSyntax.STRING_ARRAY.toString().equals(
                         settingElement.getAttributeValue( "syntax" ) );
                         settingElement.getAttributeValue( "syntax" ) );
-                final List<Element> valueElements = settingElement.getChildren( "value" );
-                for ( final Element loopValueElement : valueElements )
+                final List<XmlElement> valueElements = settingElement.getChildren( "value" );
+                for ( final XmlElement loopValueElement : valueElements )
                 {
                 {
                     final String stringValue = loopValueElement.getText();
                     final String stringValue = loopValueElement.getText();
                     if ( !StringUtil.isEmpty( stringValue ) )
                     if ( !StringUtil.isEmpty( stringValue ) )
@@ -110,7 +111,7 @@ public class ActionValue extends AbstractValue implements StoredValue
                         {
                         {
                             if ( oldType )
                             if ( oldType )
                             {
                             {
-                                if ( loopValueElement.getAttribute( "locale" ) == null )
+                                if ( loopValueElement.getAttributeValue( "locale" ) == null )
                                 {
                                 {
                                     final ActionConfigurationOldVersion1 oldVersion1 = ActionConfigurationOldVersion1.parseOldConfigString( stringValue );
                                     final ActionConfigurationOldVersion1 oldVersion1 = ActionConfigurationOldVersion1.parseOldConfigString( stringValue );
                                     values.add( convertOldVersion1Values( oldVersion1 ) );
                                     values.add( convertOldVersion1Values( oldVersion1 ) );
@@ -163,9 +164,9 @@ public class ActionValue extends AbstractValue implements StoredValue
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final ActionConfiguration value : values )
         for ( final ActionConfiguration value : values )
         {
         {
             final List<ActionConfiguration.WebAction> clonedWebActions = new ArrayList<>();
             final List<ActionConfiguration.WebAction> clonedWebActions = new ArrayList<>();
@@ -186,9 +187,9 @@ public class ActionValue extends AbstractValue implements StoredValue
             final ActionConfiguration clonedAction = value.toBuilder().webActions( clonedWebActions ).build();
             final ActionConfiguration clonedAction = value.toBuilder().webActions( clonedWebActions ).build();
 
 
 
 
-            final Element valueElement = new Element( valueElementName );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
 
 
-            valueElement.addContent( JsonUtil.serialize( clonedAction ) );
+            valueElement.addText( JsonUtil.serialize( clonedAction ) );
             returnList.add( valueElement );
             returnList.add( valueElement );
         }
         }
         return returnList;
         return returnList;
@@ -366,7 +367,7 @@ public class ActionValue extends AbstractValue implements StoredValue
         return CURRENT_SYNTAX_VERSION;
         return CURRENT_SYNTAX_VERSION;
     }
     }
 
 
-    private static int figureCurrentStoredSyntax( final Element settingElement )
+    private static int figureCurrentStoredSyntax( final XmlElement settingElement )
     {
     {
         final String storedSyntaxVersionString = settingElement.getAttributeValue( StoredConfiguration.XML_ATTRIBUTE_SYNTAX_VERSION );
         final String storedSyntaxVersionString = settingElement.getAttributeValue( StoredConfiguration.XML_ATTRIBUTE_SYNTAX_VERSION );
         if ( !StringUtil.isEmpty( storedSyntaxVersionString ) )
         if ( !StringUtil.isEmpty( storedSyntaxVersionString ) )

+ 7 - 6
server/src/main/java/password/pwm/config/value/BooleanValue.java

@@ -22,13 +22,14 @@
 
 
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
-import org.jdom2.Element;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.i18n.Display;
 import password.pwm.i18n.Display;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.io.Serializable;
 import java.io.Serializable;
@@ -55,9 +56,9 @@ public class BooleanValue implements StoredValue
                 return new BooleanValue( JsonUtil.deserialize( value, Boolean.class ) );
                 return new BooleanValue( JsonUtil.deserialize( value, Boolean.class ) );
             }
             }
 
 
-            public BooleanValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey input )
+            public BooleanValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey input )
             {
             {
-                final Element valueElement = settingElement.getChild( "value" );
+                final XmlElement valueElement = settingElement.getChild( "value" );
                 final String value = valueElement.getText();
                 final String value = valueElement.getText();
                 return new BooleanValue( Boolean.valueOf( value ) );
                 return new BooleanValue( Boolean.valueOf( value ) );
             }
             }
@@ -71,10 +72,10 @@ public class BooleanValue implements StoredValue
     }
     }
 
 
     @Override
     @Override
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final Element valueElement = new Element( valueElementName );
-        valueElement.addContent( String.valueOf( value ) );
+        final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
+        valueElement.addText( String.valueOf( value ) );
         return Collections.singletonList( valueElement );
         return Collections.singletonList( valueElement );
     }
     }
 
 

+ 10 - 11
server/src/main/java/password/pwm/config/value/ChallengeValue.java

@@ -23,13 +23,13 @@
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
-import org.jdom2.CDATA;
-import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.config.value.data.ChallengeItemConfiguration;
 import password.pwm.config.value.data.ChallengeItemConfiguration;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
@@ -70,7 +70,7 @@ public class ChallengeValue extends AbstractValue implements StoredValue
                             {
                             {
                             }
                             }
                     );
                     );
-                    srcMap = srcMap == null ? Collections.<String, List<ChallengeItemConfiguration>>emptyMap() : new TreeMap<>(
+                    srcMap = srcMap == null ? Collections.emptyMap() : new TreeMap<>(
                             srcMap );
                             srcMap );
                     return new ChallengeValue( Collections.unmodifiableMap( srcMap ) );
                     return new ChallengeValue( Collections.unmodifiableMap( srcMap ) );
                 }
                 }
@@ -78,16 +78,15 @@ public class ChallengeValue extends AbstractValue implements StoredValue
 
 
             public ChallengeValue fromXmlElement(
             public ChallengeValue fromXmlElement(
                     final PwmSetting pwmSetting,
                     final PwmSetting pwmSetting,
-                    final Element settingElement,
+                    final XmlElement settingElement,
                     final PwmSecurityKey input
                     final PwmSecurityKey input
             )
             )
             {
             {
-                final List valueElements = settingElement.getChildren( "value" );
+                final List<XmlElement> valueElements = settingElement.getChildren( "value" );
                 final Map<String, List<ChallengeItemConfiguration>> values = new TreeMap<>();
                 final Map<String, List<ChallengeItemConfiguration>> values = new TreeMap<>();
                 final boolean oldStyle = "LOCALIZED_STRING_ARRAY".equals( settingElement.getAttributeValue( "syntax" ) );
                 final boolean oldStyle = "LOCALIZED_STRING_ARRAY".equals( settingElement.getAttributeValue( "syntax" ) );
-                for ( final Object loopValue : valueElements )
+                for ( final XmlElement loopValueElement : valueElements )
                 {
                 {
-                    final Element loopValueElement = ( Element ) loopValue;
                     final String localeString = loopValueElement.getAttributeValue(
                     final String localeString = loopValueElement.getAttributeValue(
                             "locale" ) == null ? "" : loopValueElement.getAttributeValue( "locale" );
                             "locale" ) == null ? "" : loopValueElement.getAttributeValue( "locale" );
                     final String value = loopValueElement.getText();
                     final String value = loopValueElement.getText();
@@ -114,9 +113,9 @@ public class ChallengeValue extends AbstractValue implements StoredValue
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final Map.Entry<String, List<ChallengeItemConfiguration>> entry : values.entrySet() )
         for ( final Map.Entry<String, List<ChallengeItemConfiguration>> entry : values.entrySet() )
         {
         {
             final String locale = entry.getKey();
             final String locale = entry.getKey();
@@ -124,8 +123,8 @@ public class ChallengeValue extends AbstractValue implements StoredValue
             {
             {
                 if ( value != null )
                 if ( value != null )
                 {
                 {
-                    final Element valueElement = new Element( valueElementName );
-                    valueElement.addContent( new CDATA( JsonUtil.serialize( value ) ) );
+                    final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
+                    valueElement.addText( JsonUtil.serialize( value ) );
                     if ( locale != null && locale.length() > 0 )
                     if ( locale != null && locale.length() > 0 )
                     {
                     {
                         valueElement.setAttribute( "locale", locale );
                         valueElement.setAttribute( "locale", locale );

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

@@ -23,12 +23,13 @@
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
-import org.jdom2.Element;
 import password.pwm.config.CustomLinkConfiguration;
 import password.pwm.config.CustomLinkConfiguration;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -71,16 +72,15 @@ public class CustomLinkValue extends AbstractValue implements StoredValue
                 }
                 }
             }
             }
 
 
-            public CustomLinkValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey key )
+            public CustomLinkValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey key )
                     throws PwmOperationalException
                     throws PwmOperationalException
             {
             {
-                final List valueElements = settingElement.getChildren( "value" );
+                final List<XmlElement> valueElements = settingElement.getChildren( "value" );
                 final List<CustomLinkConfiguration> values = new ArrayList<>();
                 final List<CustomLinkConfiguration> values = new ArrayList<>();
-                for ( final Object loopValue : valueElements )
+                for ( final XmlElement loopValueElement  : valueElements )
                 {
                 {
-                    final Element loopValueElement = ( Element ) loopValue;
                     final String value = loopValueElement.getText();
                     final String value = loopValueElement.getText();
-                    if ( value != null && value.length() > 0 && loopValueElement.getAttribute( "locale" ) == null )
+                    if ( value != null && value.length() > 0 && loopValueElement.getAttributeValue( "locale" ) == null )
                     {
                     {
                         values.add( JsonUtil.deserialize( value, CustomLinkConfiguration.class ) );
                         values.add( JsonUtil.deserialize( value, CustomLinkConfiguration.class ) );
                     }
                     }
@@ -90,13 +90,13 @@ public class CustomLinkValue extends AbstractValue implements StoredValue
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final CustomLinkConfiguration value : values )
         for ( final CustomLinkConfiguration value : values )
         {
         {
-            final Element valueElement = new Element( valueElementName );
-            valueElement.addContent( JsonUtil.serialize( value ) );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
+            valueElement.addText( JsonUtil.serialize( value ) );
             returnList.add( valueElement );
             returnList.add( valueElement );
         }
         }
         return returnList;
         return returnList;

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

@@ -23,13 +23,15 @@
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
-import org.jdom2.Element;
+
 import password.pwm.bean.EmailItemBean;
 import password.pwm.bean.EmailItemBean;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -75,22 +77,21 @@ public class EmailValue extends AbstractValue implements StoredValue
 
 
             public EmailValue fromXmlElement(
             public EmailValue fromXmlElement(
                     final PwmSetting pwmSetting,
                     final PwmSetting pwmSetting,
-                    final Element settingElement,
+                    final XmlElement settingElement,
                     final PwmSecurityKey input
                     final PwmSecurityKey input
             )
             )
                     throws PwmOperationalException
                     throws PwmOperationalException
             {
             {
                 final Map<String, EmailItemBean> values = new TreeMap<>();
                 final Map<String, EmailItemBean> values = new TreeMap<>();
                 {
                 {
-                    final List valueElements = settingElement.getChildren( "value" );
-                    for ( final Object loopValue : valueElements )
+                    final List<XmlElement> valueElements = settingElement.getChildren( "value" );
+                    for ( final XmlElement loopValueElement : valueElements )
                     {
                     {
-                        final Element loopValueElement = ( Element ) loopValue;
                         final String value = loopValueElement.getText();
                         final String value = loopValueElement.getText();
                         if ( value != null && value.length() > 0 )
                         if ( value != null && value.length() > 0 )
                         {
                         {
-                            final String localeValue = loopValueElement.getAttribute(
-                                    "locale" ) == null ? "" : loopValueElement.getAttribute( "locale" ).getValue();
+                            final String localeValue = loopValueElement.getAttributeValue(
+                                    "locale" ) == null ? "" : loopValueElement.getAttributeValue( "locale" );
                             values.put( localeValue, JsonUtil.deserialize( value, EmailItemBean.class ) );
                             values.put( localeValue, JsonUtil.deserialize( value, EmailItemBean.class ) );
                         }
                         }
                     }
                     }
@@ -100,19 +101,19 @@ public class EmailValue extends AbstractValue implements StoredValue
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final Map.Entry<String, EmailItemBean> entry : values.entrySet() )
         for ( final Map.Entry<String, EmailItemBean> entry : values.entrySet() )
         {
         {
             final String localeValue = entry.getKey();
             final String localeValue = entry.getKey();
             final EmailItemBean emailItemBean = entry.getValue();
             final EmailItemBean emailItemBean = entry.getValue();
-            final Element valueElement = new Element( valueElementName );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
             if ( localeValue.length() > 0 )
             if ( localeValue.length() > 0 )
             {
             {
                 valueElement.setAttribute( "locale", localeValue );
                 valueElement.setAttribute( "locale", localeValue );
             }
             }
-            valueElement.addContent( JsonUtil.serialize( emailItemBean ) );
+            valueElement.addText( JsonUtil.serialize( emailItemBean ) );
             returnList.add( valueElement );
             returnList.add( valueElement );
         }
         }
         return returnList;
         return returnList;

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

@@ -23,7 +23,6 @@
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
 import lombok.Value;
 import lombok.Value;
-import org.jdom2.Element;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
@@ -32,6 +31,8 @@ import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.http.bean.ImmutableByteArray;
 import password.pwm.http.bean.ImmutableByteArray;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmHashAlgorithm;
 import password.pwm.util.secure.PwmHashAlgorithm;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
@@ -125,23 +126,21 @@ public class FileValue extends AbstractValue implements StoredValue
         return new StoredValueFactory()
         return new StoredValueFactory()
         {
         {
 
 
-            public FileValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey input )
+            public FileValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey input )
                     throws PwmOperationalException
                     throws PwmOperationalException
             {
             {
-                final List valueElements = settingElement.getChildren( "value" );
+                final List<XmlElement> valueElements = settingElement.getChildren( "value" );
                 final Map<FileInformation, FileContent> values = new LinkedHashMap<>();
                 final Map<FileInformation, FileContent> values = new LinkedHashMap<>();
-                for ( final Object loopValue : valueElements )
+                for ( final XmlElement loopValueElement : valueElements )
                 {
                 {
-                    final Element loopValueElement = ( Element ) loopValue;
-
-                    final Element loopFileInformation = loopValueElement.getChild( "FileInformation" );
+                    final XmlElement loopFileInformation = loopValueElement.getChild( "FileInformation" );
                     if ( loopFileInformation != null )
                     if ( loopFileInformation != null )
                     {
                     {
                         final String loopFileInformationJson = loopFileInformation.getText();
                         final String loopFileInformationJson = loopFileInformation.getText();
                         final FileInformation fileInformation = JsonUtil.deserialize( loopFileInformationJson,
                         final FileInformation fileInformation = JsonUtil.deserialize( loopFileInformationJson,
                                 FileInformation.class );
                                 FileInformation.class );
 
 
-                        final Element loopFileContentElement = loopValueElement.getChild( "FileContent" );
+                        final XmlElement loopFileContentElement = loopValueElement.getChild( "FileContent" );
                         if ( loopFileContentElement != null )
                         if ( loopFileContentElement != null )
                         {
                         {
                             final String fileContentString = loopFileContentElement.getText();
                             final String fileContentString = loopFileContentElement.getText();
@@ -168,23 +167,23 @@ public class FileValue extends AbstractValue implements StoredValue
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final Map.Entry<FileInformation, FileContent> entry : this.values.entrySet() )
         for ( final Map.Entry<FileInformation, FileContent> entry : this.values.entrySet() )
         {
         {
             final FileValue.FileInformation fileInformation = entry.getKey();
             final FileValue.FileInformation fileInformation = entry.getKey();
             final FileContent fileContent = entry.getValue();
             final FileContent fileContent = entry.getValue();
-            final Element valueElement = new Element( valueElementName );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
 
 
-            final Element fileInformationElement = new Element( "FileInformation" );
-            fileInformationElement.addContent( JsonUtil.serialize( fileInformation ) );
+            final XmlElement fileInformationElement = XmlFactory.getFactory().newElement( "FileInformation" );
+            fileInformationElement.addText( JsonUtil.serialize( fileInformation ) );
             valueElement.addContent( fileInformationElement );
             valueElement.addContent( fileInformationElement );
 
 
-            final Element fileContentElement = new Element( "FileContent" );
+            final XmlElement fileContentElement = XmlFactory.getFactory().newElement( "FileContent" );
             try
             try
             {
             {
-                fileContentElement.addContent( fileContent.toEncodedString() );
+                fileContentElement.addText( fileContent.toEncodedString() );
             }
             }
             catch ( IOException e )
             catch ( IOException e )
             {
             {

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

@@ -23,7 +23,6 @@
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
-import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSettingSyntax;
 import password.pwm.config.PwmSettingSyntax;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
@@ -32,6 +31,8 @@ import password.pwm.error.PwmOperationalException;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -76,18 +77,17 @@ public class FormValue extends AbstractValue implements StoredValue
                 }
                 }
             }
             }
 
 
-            public FormValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey key )
+            public FormValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey key )
                     throws PwmOperationalException
                     throws PwmOperationalException
             {
             {
                 final boolean oldType = PwmSettingSyntax.LOCALIZED_STRING_ARRAY.toString().equals(
                 final boolean oldType = PwmSettingSyntax.LOCALIZED_STRING_ARRAY.toString().equals(
                         settingElement.getAttributeValue( "syntax" ) );
                         settingElement.getAttributeValue( "syntax" ) );
-                final List valueElements = settingElement.getChildren( "value" );
+                final List<XmlElement> valueElements = settingElement.getChildren( "value" );
                 final List<FormConfiguration> values = new ArrayList<>();
                 final List<FormConfiguration> values = new ArrayList<>();
-                for ( final Object loopValue : valueElements )
+                for ( final XmlElement loopValueElement  : valueElements )
                 {
                 {
-                    final Element loopValueElement = ( Element ) loopValue;
                     final String value = loopValueElement.getText();
                     final String value = loopValueElement.getText();
-                    if ( value != null && value.length() > 0 && loopValueElement.getAttribute( "locale" ) == null )
+                    if ( value != null && value.length() > 0 && loopValueElement.getAttributeValue( "locale" ) == null )
                     {
                     {
                         if ( oldType )
                         if ( oldType )
                         {
                         {
@@ -106,13 +106,13 @@ public class FormValue extends AbstractValue implements StoredValue
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final FormConfiguration value : values )
         for ( final FormConfiguration value : values )
         {
         {
-            final Element valueElement = new Element( valueElementName );
-            valueElement.addContent( JsonUtil.serialize( value ) );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
+            valueElement.addText( JsonUtil.serialize( value ) );
             returnList.add( valueElement );
             returnList.add( valueElement );
         }
         }
         return returnList;
         return returnList;

+ 10 - 11
server/src/main/java/password/pwm/config/value/LocalizedStringArrayValue.java

@@ -23,12 +23,12 @@
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
-import org.jdom2.CDATA;
-import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -57,7 +57,7 @@ public class LocalizedStringArrayValue extends AbstractValue implements StoredVa
             {
             {
                 if ( input == null )
                 if ( input == null )
                 {
                 {
-                    return new LocalizedStringArrayValue( Collections.<String, List<String>>emptyMap() );
+                    return new LocalizedStringArrayValue( Collections.emptyMap() );
                 }
                 }
                 else
                 else
                 {
                 {
@@ -69,13 +69,12 @@ public class LocalizedStringArrayValue extends AbstractValue implements StoredVa
                 }
                 }
             }
             }
 
 
-            public LocalizedStringArrayValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey key )
+            public LocalizedStringArrayValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey key )
             {
             {
-                final List valueElements = settingElement.getChildren( "value" );
+                final List<XmlElement> valueElements = settingElement.getChildren( "value" );
                 final Map<String, List<String>> values = new TreeMap<>();
                 final Map<String, List<String>> values = new TreeMap<>();
-                for ( final Object loopValue : valueElements )
+                for ( final XmlElement loopValueElement  : valueElements )
                 {
                 {
-                    final Element loopValueElement = ( Element ) loopValue;
                     final String localeString = loopValueElement.getAttributeValue(
                     final String localeString = loopValueElement.getAttributeValue(
                             "locale" ) == null ? "" : loopValueElement.getAttributeValue( "locale" );
                             "locale" ) == null ? "" : loopValueElement.getAttributeValue( "locale" );
                     final String value = loopValueElement.getText();
                     final String value = loopValueElement.getText();
@@ -92,16 +91,16 @@ public class LocalizedStringArrayValue extends AbstractValue implements StoredVa
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final Map.Entry<String, List<String>> entry : values.entrySet() )
         for ( final Map.Entry<String, List<String>> entry : values.entrySet() )
         {
         {
             final String locale = entry.getKey();
             final String locale = entry.getKey();
             for ( final String value : entry.getValue() )
             for ( final String value : entry.getValue() )
             {
             {
-                final Element valueElement = new Element( valueElementName );
-                valueElement.addContent( new CDATA( value ) );
+                final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
+                valueElement.addText( value );
                 if ( locale != null && locale.length() > 0 )
                 if ( locale != null && locale.length() > 0 )
                 {
                 {
                     valueElement.setAttribute( "locale", locale );
                     valueElement.setAttribute( "locale", locale );

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

@@ -23,12 +23,12 @@
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
-import org.jdom2.CDATA;
-import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -69,13 +69,12 @@ public class LocalizedStringValue extends AbstractValue implements StoredValue
                 }
                 }
             }
             }
 
 
-            public LocalizedStringValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey key )
+            public LocalizedStringValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey key )
             {
             {
-                final List elements = settingElement.getChildren( "value" );
+                final List<XmlElement> elements = settingElement.getChildren( "value" );
                 final Map<String, String> values = new TreeMap<>();
                 final Map<String, String> values = new TreeMap<>();
-                for ( final Object loopValue : elements )
+                for ( final XmlElement loopValueElement : elements )
                 {
                 {
-                    final Element loopValueElement = ( Element ) loopValue;
                     final String localeString = loopValueElement.getAttributeValue( "locale" );
                     final String localeString = loopValueElement.getAttributeValue( "locale" );
                     final String value = loopValueElement.getText();
                     final String value = loopValueElement.getText();
                     values.put( localeString == null ? "" : localeString, value );
                     values.put( localeString == null ? "" : localeString, value );
@@ -85,15 +84,15 @@ public class LocalizedStringValue extends AbstractValue implements StoredValue
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final Map.Entry<String, String> entry : value.entrySet() )
         for ( final Map.Entry<String, String> entry : value.entrySet() )
         {
         {
             final String locale = entry.getKey();
             final String locale = entry.getKey();
             final String loopValue = entry.getValue();
             final String loopValue = entry.getValue();
-            final Element valueElement = new Element( valueElementName );
-            valueElement.addContent( new CDATA( loopValue ) );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
+            valueElement.addText( loopValue );
             if ( locale != null && locale.length() > 0 )
             if ( locale != null && locale.length() > 0 )
             {
             {
                 valueElement.setAttribute( "locale", locale );
                 valueElement.setAttribute( "locale", locale );

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

@@ -23,7 +23,6 @@
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
-import org.jdom2.Element;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
@@ -35,6 +34,8 @@ import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmBlockAlgorithm;
 import password.pwm.util.secure.PwmBlockAlgorithm;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.SecureEngine;
 import password.pwm.util.secure.SecureEngine;
@@ -88,30 +89,30 @@ public class NamedSecretValue implements StoredValue
 
 
             public NamedSecretValue fromXmlElement(
             public NamedSecretValue fromXmlElement(
                     final PwmSetting pwmSetting,
                     final PwmSetting pwmSetting,
-                    final Element settingElement,
+                    final XmlElement settingElement,
                     final PwmSecurityKey key
                     final PwmSecurityKey key
             )
             )
                     throws PwmOperationalException, PwmUnrecoverableException
                     throws PwmOperationalException, PwmUnrecoverableException
             {
             {
                 final Map<String, NamedSecretData> values = new LinkedHashMap<>();
                 final Map<String, NamedSecretData> values = new LinkedHashMap<>();
-                final List<Element> valueElements = settingElement.getChildren( "value" );
+                final List<XmlElement> valueElements = settingElement.getChildren( "value" );
 
 
                 try
                 try
                 {
                 {
                     if ( valueElements != null )
                     if ( valueElements != null )
                     {
                     {
-                        for ( final Element value : valueElements )
+                        for ( final XmlElement value : valueElements )
                         {
                         {
                             if ( value.getChild( ELEMENT_NAME ) != null && value.getChild( ELEMENT_PASSWORD ) != null )
                             if ( value.getChild( ELEMENT_NAME ) != null && value.getChild( ELEMENT_PASSWORD ) != null )
                             {
                             {
                                 final String name = value.getChild( ELEMENT_NAME ).getText();
                                 final String name = value.getChild( ELEMENT_NAME ).getText();
                                 final String encodedValue = value.getChild( ELEMENT_PASSWORD ).getText();
                                 final String encodedValue = value.getChild( ELEMENT_PASSWORD ).getText();
                                 final PasswordData passwordData = new PasswordData( SecureEngine.decryptStringValue( encodedValue, key, PwmBlockAlgorithm.CONFIG ) );
                                 final PasswordData passwordData = new PasswordData( SecureEngine.decryptStringValue( encodedValue, key, PwmBlockAlgorithm.CONFIG ) );
-                                final List<Element> usages = value.getChildren( ELEMENT_USAGE );
+                                final List<XmlElement> usages = value.getChildren( ELEMENT_USAGE );
                                 final List<String> strUsages = new ArrayList<>();
                                 final List<String> strUsages = new ArrayList<>();
                                 if ( usages != null )
                                 if ( usages != null )
                                 {
                                 {
-                                    for ( final Element usageElement : usages )
+                                    for ( final XmlElement usageElement : usages )
                                     {
                                     {
                                         strUsages.add( usageElement.getText() );
                                         strUsages.add( usageElement.getText() );
                                     }
                                     }
@@ -132,7 +133,7 @@ public class NamedSecretValue implements StoredValue
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName )
+    public List<XmlElement> toXmlValues( final String valueElementName )
     {
     {
         throw new IllegalStateException( "password xml output requires hash key" );
         throw new IllegalStateException( "password xml output requires hash key" );
     }
     }
@@ -155,14 +156,14 @@ public class NamedSecretValue implements StoredValue
         return 0;
         return 0;
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey key )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey key )
     {
     {
         if ( values == null )
         if ( values == null )
         {
         {
-            final Element valueElement = new Element( valueElementName );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
             return Collections.singletonList( valueElement );
             return Collections.singletonList( valueElement );
         }
         }
-        final List<Element> valuesElement = new ArrayList<>();
+        final List<XmlElement> valuesElement = new ArrayList<>();
         try
         try
         {
         {
             for ( final Map.Entry<String, NamedSecretData> entry : values.entrySet() )
             for ( final Map.Entry<String, NamedSecretData> entry : values.entrySet() )
@@ -170,19 +171,19 @@ public class NamedSecretValue implements StoredValue
                 final String name = entry.getKey();
                 final String name = entry.getKey();
                 final PasswordData passwordData = entry.getValue().getPassword();
                 final PasswordData passwordData = entry.getValue().getPassword();
                 final String encodedValue = SecureEngine.encryptToString( passwordData.getStringValue(), key, PwmBlockAlgorithm.CONFIG );
                 final String encodedValue = SecureEngine.encryptToString( passwordData.getStringValue(), key, PwmBlockAlgorithm.CONFIG );
-                final Element newValueElement = new Element( "value" );
-                final Element nameElement = new Element( ELEMENT_NAME );
-                nameElement.setText( name );
-                final Element encodedValueElement = new Element( ELEMENT_PASSWORD );
-                encodedValueElement.setText( encodedValue );
+                final XmlElement newValueElement = XmlFactory.getFactory().newElement( "value" );
+                final XmlElement nameElement = XmlFactory.getFactory().newElement( ELEMENT_NAME );
+                nameElement.addText( name );
+                final XmlElement encodedValueElement = XmlFactory.getFactory().newElement( ELEMENT_PASSWORD );
+                encodedValueElement.addText( encodedValue );
 
 
                 newValueElement.addContent( nameElement );
                 newValueElement.addContent( nameElement );
                 newValueElement.addContent( encodedValueElement );
                 newValueElement.addContent( encodedValueElement );
 
 
                 for ( final String usages : values.get( name ).getUsage() )
                 for ( final String usages : values.get( name ).getUsage() )
                 {
                 {
-                    final Element usageElement = new Element( ELEMENT_USAGE );
-                    usageElement.setText( usages );
+                    final XmlElement usageElement = XmlFactory.getFactory().newElement( ELEMENT_USAGE );
+                    usageElement.addText( usages );
                     newValueElement.addContent( usageElement );
                     newValueElement.addContent( usageElement );
                 }
                 }
 
 

+ 9 - 8
server/src/main/java/password/pwm/config/value/NumericArrayValue.java

@@ -22,11 +22,12 @@
 
 
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
-import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -57,11 +58,11 @@ public class NumericArrayValue extends AbstractValue implements StoredValue
                 return new NumericArrayValue( list );
                 return new NumericArrayValue( list );
             }
             }
 
 
-            public NumericArrayValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey input )
+            public NumericArrayValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey input )
             {
             {
                 final List<Long> returnList = new ArrayList<>(  );
                 final List<Long> returnList = new ArrayList<>(  );
-                final List<Element> valueElements = settingElement.getChildren( "value" );
-                for ( final Element element : valueElements )
+                final List<XmlElement> valueElements = settingElement.getChildren( "value" );
+                for ( final XmlElement element : valueElements )
                 {
                 {
                     final String strValue = element.getText();
                     final String strValue = element.getText();
                     final Long longValue = Long.parseLong( strValue );
                     final Long longValue = Long.parseLong( strValue );
@@ -73,13 +74,13 @@ public class NumericArrayValue extends AbstractValue implements StoredValue
     }
     }
 
 
     @Override
     @Override
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final Long value : this.values )
         for ( final Long value : this.values )
         {
         {
-            final Element valueElement = new Element( valueElementName );
-            valueElement.addContent( String.valueOf( value ) );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
+            valueElement.addText( String.valueOf( value ) );
             returnList.add( valueElement );
             returnList.add( valueElement );
         }
         }
         return returnList;
         return returnList;

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

@@ -22,11 +22,12 @@
 
 
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
-import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSettingProperty;
 import password.pwm.config.PwmSettingProperty;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.util.Collections;
 import java.util.Collections;
@@ -50,9 +51,9 @@ public class NumericValue extends AbstractValue implements StoredValue
                 return new NumericValue( JsonUtil.deserialize( value, Long.class ) );
                 return new NumericValue( JsonUtil.deserialize( value, Long.class ) );
             }
             }
 
 
-            public NumericValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey input )
+            public NumericValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey input )
             {
             {
-                final Element valueElement = settingElement.getChild( "value" );
+                final XmlElement valueElement = settingElement.getChild( "value" );
                 final String value = valueElement.getText();
                 final String value = valueElement.getText();
                 return new NumericValue( normalizeValue( pwmSetting, Long.parseLong( value ) ) );
                 return new NumericValue( normalizeValue( pwmSetting, Long.parseLong( value ) ) );
             }
             }
@@ -78,10 +79,10 @@ public class NumericValue extends AbstractValue implements StoredValue
     }
     }
 
 
     @Override
     @Override
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final Element valueElement = new Element( valueElementName );
-        valueElement.addContent( Long.toString( value ) );
+        final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
+        valueElement.addText( Long.toString( value ) );
         return Collections.singletonList( valueElement );
         return Collections.singletonList( valueElement );
     }
     }
 
 

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

@@ -23,11 +23,12 @@
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
-import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -71,14 +72,13 @@ public class OptionListValue extends AbstractValue implements StoredValue
                 }
                 }
             }
             }
 
 
-            public OptionListValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey key )
+            public OptionListValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey key )
                     throws PwmOperationalException
                     throws PwmOperationalException
             {
             {
-                final List valueElements = settingElement.getChildren( "value" );
+                final List<XmlElement> valueElements = settingElement.getChildren( "value" );
                 final Set<String> values = new TreeSet<>();
                 final Set<String> values = new TreeSet<>();
-                for ( final Object loopValue : valueElements )
+                for ( final XmlElement loopValueElement : valueElements )
                 {
                 {
-                    final Element loopValueElement = ( Element ) loopValue;
                     final String value = loopValueElement.getText();
                     final String value = loopValueElement.getText();
                     if ( value != null && !value.trim().isEmpty() )
                     if ( value != null && !value.trim().isEmpty() )
                     {
                     {
@@ -90,13 +90,13 @@ public class OptionListValue extends AbstractValue implements StoredValue
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final String value : values )
         for ( final String value : values )
         {
         {
-            final Element valueElement = new Element( valueElementName );
-            valueElement.addContent( value );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
+            valueElement.addText( value );
             returnList.add( valueElement );
             returnList.add( valueElement );
         }
         }
         return returnList;
         return returnList;

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

@@ -22,7 +22,7 @@
 
 
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
-import org.jdom2.Element;
+
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
@@ -32,6 +32,8 @@ import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.util.PasswordData;
 import password.pwm.util.PasswordData;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmBlockAlgorithm;
 import password.pwm.util.secure.PwmBlockAlgorithm;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.SecureEngine;
 import password.pwm.util.secure.SecureEngine;
@@ -80,12 +82,12 @@ public class PasswordValue implements StoredValue
 
 
             public PasswordValue fromXmlElement(
             public PasswordValue fromXmlElement(
                     final PwmSetting pwmSetting,
                     final PwmSetting pwmSetting,
-                    final Element settingElement,
+                    final XmlElement settingElement,
                     final PwmSecurityKey key
                     final PwmSecurityKey key
             )
             )
                     throws PwmOperationalException, PwmUnrecoverableException
                     throws PwmOperationalException, PwmUnrecoverableException
             {
             {
-                final Element valueElement = settingElement.getChild( "value" );
+                final XmlElement valueElement = settingElement.getChild( "value" );
                 final String rawValue = valueElement.getText();
                 final String rawValue = valueElement.getText();
 
 
                 final PasswordValue newPasswordValue = new PasswordValue();
                 final PasswordValue newPasswordValue = new PasswordValue();
@@ -124,7 +126,7 @@ public class PasswordValue implements StoredValue
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName )
+    public List<XmlElement> toXmlValues( final String valueElementName )
     {
     {
         throw new IllegalStateException( "password xml output requires hash key" );
         throw new IllegalStateException( "password xml output requires hash key" );
     }
     }
@@ -147,22 +149,21 @@ public class PasswordValue implements StoredValue
         return 0;
         return 0;
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey key )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey key )
     {
     {
         if ( value == null )
         if ( value == null )
         {
         {
-            final Element valueElement = new Element( valueElementName );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
             return Collections.singletonList( valueElement );
             return Collections.singletonList( valueElement );
         }
         }
-        final Element valueElement = new Element( valueElementName );
+        final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
         try
         try
         {
         {
             final String encodedValue = SecureEngine.encryptToString( value.getStringValue(), key, PwmBlockAlgorithm.CONFIG );
             final String encodedValue = SecureEngine.encryptToString( value.getStringValue(), key, PwmBlockAlgorithm.CONFIG );
-            valueElement.addContent( encodedValue );
+            valueElement.addText( encodedValue );
         }
         }
         catch ( Exception e )
         catch ( Exception e )
         {
         {
-            valueElement.addContent( "" );
             throw new RuntimeException( "missing required AES and SHA1 libraries, or other crypto fault: " + e.getMessage() );
             throw new RuntimeException( "missing required AES and SHA1 libraries, or other crypto fault: " + e.getMessage() );
         }
         }
         return Collections.singletonList( valueElement );
         return Collections.singletonList( valueElement );

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

@@ -22,12 +22,13 @@
 
 
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
-import org.jdom2.Element;
 import password.pwm.bean.PrivateKeyCertificate;
 import password.pwm.bean.PrivateKeyCertificate;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmBlockAlgorithm;
 import password.pwm.util.secure.PwmBlockAlgorithm;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
@@ -59,16 +60,16 @@ public class PrivateKeyValue extends AbstractValue
     {
     {
         return new StoredValue.StoredValueFactory()
         return new StoredValue.StoredValueFactory()
         {
         {
-            public PrivateKeyValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey key )
+            public PrivateKeyValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey key )
             {
             {
                 if ( settingElement != null && settingElement.getChild( "value" ) != null )
                 if ( settingElement != null && settingElement.getChild( "value" ) != null )
                 {
                 {
 
 
-                    final Element valueElement = settingElement.getChild( "value" );
+                    final XmlElement valueElement = settingElement.getChild( "value" );
                     if ( valueElement != null )
                     if ( valueElement != null )
                     {
                     {
                         final List<X509Certificate> certificates = new ArrayList<>();
                         final List<X509Certificate> certificates = new ArrayList<>();
-                        for ( final Element certificateElement : valueElement.getChildren( ELEMENT_NAME_CERTIFICATE ) )
+                        for ( final XmlElement certificateElement : valueElement.getChildren( ELEMENT_NAME_CERTIFICATE ) )
                         {
                         {
                             try
                             try
                             {
                             {
@@ -87,7 +88,7 @@ public class PrivateKeyValue extends AbstractValue
                         PrivateKey privateKey = null;
                         PrivateKey privateKey = null;
                         try
                         try
                         {
                         {
-                            final Element keyElement = valueElement.getChild( ELEMENT_NAME_KEY );
+                            final XmlElement keyElement = valueElement.getChild( ELEMENT_NAME_KEY );
                             final String encryptedText = keyElement.getText();
                             final String encryptedText = keyElement.getText();
                             final String decryptedText = SecureEngine.decryptStringValue( encryptedText, key, PwmBlockAlgorithm.CONFIG );
                             final String decryptedText = SecureEngine.decryptStringValue( encryptedText, key, PwmBlockAlgorithm.CONFIG );
                             final byte[] privateKeyBytes = StringUtil.base64Decode( decryptedText );
                             final byte[] privateKeyBytes = StringUtil.base64Decode( decryptedText );
@@ -121,7 +122,7 @@ public class PrivateKeyValue extends AbstractValue
     }
     }
 
 
 
 
-    public List<Element> toXmlValues( final String valueElementName )
+    public List<XmlElement> toXmlValues( final String valueElementName )
     {
     {
         throw new IllegalStateException( "password xml output requires hash key" );
         throw new IllegalStateException( "password xml output requires hash key" );
     }
     }
@@ -144,9 +145,9 @@ public class PrivateKeyValue extends AbstractValue
         return 0;
         return 0;
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey key )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey key )
     {
     {
-        final Element valueElement = new Element( "value" );
+        final XmlElement valueElement = XmlFactory.getFactory().newElement( "value" );
         if ( privateKeyCertificate != null )
         if ( privateKeyCertificate != null )
         {
         {
             try
             try
@@ -154,22 +155,21 @@ public class PrivateKeyValue extends AbstractValue
                 {
                 {
                     for ( final X509Certificate certificate : privateKeyCertificate.getCertificates() )
                     for ( final X509Certificate certificate : privateKeyCertificate.getCertificates() )
                     {
                     {
-                        final Element certificateElement = new Element( ELEMENT_NAME_CERTIFICATE );
-                        certificateElement.setText( X509Utils.certificateToBase64( certificate ) );
+                        final XmlElement certificateElement = XmlFactory.getFactory().newElement( ELEMENT_NAME_CERTIFICATE );
+                        certificateElement.addText( X509Utils.certificateToBase64( certificate ) );
                         valueElement.addContent( certificateElement );
                         valueElement.addContent( certificateElement );
                     }
                     }
                 }
                 }
                 {
                 {
-                    final Element keyElement = new Element( ELEMENT_NAME_KEY );
+                    final XmlElement keyElement = XmlFactory.getFactory().newElement( ELEMENT_NAME_KEY );
                     final String b64EncodedKey = StringUtil.base64Encode( privateKeyCertificate.getKey().getEncoded() );
                     final String b64EncodedKey = StringUtil.base64Encode( privateKeyCertificate.getKey().getEncoded() );
                     final String encryptedKey = SecureEngine.encryptToString( b64EncodedKey, key, PwmBlockAlgorithm.CONFIG );
                     final String encryptedKey = SecureEngine.encryptToString( b64EncodedKey, key, PwmBlockAlgorithm.CONFIG );
-                    keyElement.setText( encryptedKey );
+                    keyElement.addText( encryptedKey );
                     valueElement.addContent( keyElement );
                     valueElement.addContent( keyElement );
                 }
                 }
             }
             }
             catch ( Exception e )
             catch ( Exception e )
             {
             {
-                valueElement.addContent( "" );
                 throw new RuntimeException( "missing required AES and SHA1 libraries, or other crypto fault: " + e.getMessage() );
                 throw new RuntimeException( "missing required AES and SHA1 libraries, or other crypto fault: " + e.getMessage() );
             }
             }
         }
         }

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

@@ -23,7 +23,6 @@
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
-import org.jdom2.Element;
 import password.pwm.PwmConstants;
 import password.pwm.PwmConstants;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
@@ -31,6 +30,8 @@ import password.pwm.config.value.data.RemoteWebServiceConfiguration;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.StringUtil;
 import password.pwm.util.java.StringUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.X509Utils;
 import password.pwm.util.secure.X509Utils;
@@ -83,16 +84,15 @@ public class RemoteWebServiceValue extends AbstractValue implements StoredValue
 
 
             public RemoteWebServiceValue fromXmlElement(
             public RemoteWebServiceValue fromXmlElement(
                     final PwmSetting pwmSetting,
                     final PwmSetting pwmSetting,
-                    final Element settingElement,
+                    final XmlElement settingElement,
                     final PwmSecurityKey pwmSecurityKey
                     final PwmSecurityKey pwmSecurityKey
             )
             )
                     throws PwmOperationalException
                     throws PwmOperationalException
             {
             {
-                final List valueElements = settingElement.getChildren( "value" );
+                final List<XmlElement> valueElements = settingElement.getChildren( "value" );
                 final List<RemoteWebServiceConfiguration> values = new ArrayList<>();
                 final List<RemoteWebServiceConfiguration> values = new ArrayList<>();
-                for ( final Object loopValue : valueElements )
+                for ( final XmlElement loopValueElement : valueElements )
                 {
                 {
-                    final Element loopValueElement = ( Element ) loopValue;
                     final String value = loopValueElement.getText();
                     final String value = loopValueElement.getText();
                     if ( value != null && value.length() > 0 )
                     if ( value != null && value.length() > 0 )
                     {
                     {
@@ -106,12 +106,12 @@ public class RemoteWebServiceValue extends AbstractValue implements StoredValue
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final RemoteWebServiceConfiguration value : values )
         for ( final RemoteWebServiceConfiguration value : values )
         {
         {
-            final Element valueElement = new Element( valueElementName );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
             final RemoteWebServiceConfiguration clonedValue = JsonUtil.cloneUsingJson( value, RemoteWebServiceConfiguration.class );
             final RemoteWebServiceConfiguration clonedValue = JsonUtil.cloneUsingJson( value, RemoteWebServiceConfiguration.class );
             try
             try
             {
             {
@@ -122,7 +122,7 @@ public class RemoteWebServiceValue extends AbstractValue implements StoredValue
                 LOGGER.warn( "error decoding stored pw value: " + e.getMessage() );
                 LOGGER.warn( "error decoding stored pw value: " + e.getMessage() );
             }
             }
 
 
-            valueElement.addContent( JsonUtil.serialize( clonedValue ) );
+            valueElement.addText( JsonUtil.serialize( clonedValue ) );
             returnList.add( valueElement );
             returnList.add( valueElement );
         }
         }
         return returnList;
         return returnList;

+ 11 - 12
server/src/main/java/password/pwm/config/value/StringArrayValue.java

@@ -22,11 +22,11 @@
 
 
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
-import org.jdom2.CDATA;
-import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -54,12 +54,12 @@ public class StringArrayValue extends AbstractValue implements StoredValue
             {
             {
                 if ( input == null )
                 if ( input == null )
                 {
                 {
-                    return new StringArrayValue( Collections.<String>emptyList() );
+                    return new StringArrayValue( Collections.emptyList() );
                 }
                 }
                 else
                 else
                 {
                 {
                     List<String> srcList = JsonUtil.deserializeStringList( input );
                     List<String> srcList = JsonUtil.deserializeStringList( input );
-                    srcList = srcList == null ? Collections.<String>emptyList() : srcList;
+                    srcList = srcList == null ? Collections.emptyList() : srcList;
                     while ( srcList.contains( null ) )
                     while ( srcList.contains( null ) )
                     {
                     {
                         srcList.remove( null );
                         srcList.remove( null );
@@ -68,13 +68,12 @@ public class StringArrayValue extends AbstractValue implements StoredValue
                 }
                 }
             }
             }
 
 
-            public StringArrayValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey key )
+            public StringArrayValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey key )
             {
             {
-                final List valueElements = settingElement.getChildren( "value" );
+                final List<XmlElement> valueElements = settingElement.getChildren( "value" );
                 final List<String> values = new ArrayList<>();
                 final List<String> values = new ArrayList<>();
-                for ( final Object loopValue : valueElements )
+                for ( final XmlElement loopValueElement : valueElements )
                 {
                 {
-                    final Element loopValueElement = ( Element ) loopValue;
                     final String value = loopValueElement.getText();
                     final String value = loopValueElement.getText();
                     values.add( value );
                     values.add( value );
                 }
                 }
@@ -83,13 +82,13 @@ public class StringArrayValue extends AbstractValue implements StoredValue
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final String value : this.values )
         for ( final String value : this.values )
         {
         {
-            final Element valueElement = new Element( valueElementName );
-            valueElement.addContent( new CDATA( value ) );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
+            valueElement.addText( value );
             returnList.add( valueElement );
             returnList.add( valueElement );
         }
         }
         return returnList;
         return returnList;

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

@@ -22,13 +22,13 @@
 
 
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
-import org.jdom2.CDATA;
-import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSettingFlag;
 import password.pwm.config.PwmSettingFlag;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.config.value.data.FormConfiguration;
 import password.pwm.config.value.data.FormConfiguration;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.util.Collections;
 import java.util.Collections;
@@ -60,18 +60,18 @@ public class StringValue extends AbstractValue implements StoredValue
                 return new StringValue( newValue );
                 return new StringValue( newValue );
             }
             }
 
 
-            public StringValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey key )
+            public StringValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey key )
             {
             {
-                final Element valueElement = settingElement.getChild( "value" );
+                final XmlElement valueElement = settingElement.getChild( "value" );
                 return new StringValue( valueElement == null ? "" : valueElement.getText() );
                 return new StringValue( valueElement == null ? "" : valueElement.getText() );
             }
             }
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final Element valueElement = new Element( valueElementName );
-        valueElement.addContent( new CDATA( value ) );
+        final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
+        valueElement.addText( value );
         return Collections.singletonList( valueElement );
         return Collections.singletonList( valueElement );
     }
     }
 
 

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

@@ -24,7 +24,6 @@ package password.pwm.config.value;
 
 
 import com.google.gson.reflect.TypeToken;
 import com.google.gson.reflect.TypeToken;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.config.stored.StoredConfigurationImpl;
 import password.pwm.config.stored.StoredConfigurationImpl;
@@ -32,6 +31,8 @@ import password.pwm.config.value.data.UserPermission;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.i18n.Display;
 import password.pwm.i18n.Display;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
@@ -74,16 +75,15 @@ public class UserPermissionValue extends AbstractValue implements StoredValue
                 }
                 }
             }
             }
 
 
-            public UserPermissionValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey key )
+            public UserPermissionValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey key )
                     throws PwmOperationalException
                     throws PwmOperationalException
             {
             {
                 final boolean newType = "2".equals(
                 final boolean newType = "2".equals(
                         settingElement.getAttributeValue( StoredConfigurationImpl.XML_ATTRIBUTE_SYNTAX_VERSION ) );
                         settingElement.getAttributeValue( StoredConfigurationImpl.XML_ATTRIBUTE_SYNTAX_VERSION ) );
-                final List valueElements = settingElement.getChildren( "value" );
+                final List<XmlElement> valueElements = settingElement.getChildren( "value" );
                 final List<UserPermission> values = new ArrayList<>();
                 final List<UserPermission> values = new ArrayList<>();
-                for ( final Object loopValue : valueElements )
+                for ( final XmlElement loopValueElement : valueElements )
                 {
                 {
-                    final Element loopValueElement = ( Element ) loopValue;
                     final String value = loopValueElement.getText();
                     final String value = loopValueElement.getText();
                     if ( value != null && !value.isEmpty() )
                     if ( value != null && !value.isEmpty() )
                     {
                     {
@@ -105,13 +105,13 @@ public class UserPermissionValue extends AbstractValue implements StoredValue
         };
         };
     }
     }
 
 
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final UserPermission value : values )
         for ( final UserPermission value : values )
         {
         {
-            final Element valueElement = new Element( valueElementName );
-            valueElement.addContent( JsonUtil.serialize( value ) );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
+            valueElement.addText( JsonUtil.serialize( value ) );
             returnList.add( valueElement );
             returnList.add( valueElement );
         }
         }
         return returnList;
         return returnList;

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

@@ -22,12 +22,12 @@
 
 
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
-import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.ErrorInformation;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmError;
 import password.pwm.error.PwmOperationalException;
 import password.pwm.error.PwmOperationalException;
+import password.pwm.util.java.XmlElement;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
@@ -57,7 +57,7 @@ public class ValueFactory
         }
         }
     }
     }
 
 
-    public static StoredValue fromXmlValues( final PwmSetting setting, final Element settingElement, final PwmSecurityKey key )
+    public static StoredValue fromXmlValues( final PwmSetting setting, final XmlElement settingElement, final PwmSecurityKey key )
     {
     {
         try
         try
         {
         {

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

@@ -23,8 +23,6 @@
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
 import lombok.Value;
 import lombok.Value;
-import org.jdom2.CDATA;
-import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.config.option.IdentityVerificationMethod;
 import password.pwm.config.option.IdentityVerificationMethod;
@@ -32,6 +30,8 @@ import password.pwm.error.PwmOperationalException;
 import password.pwm.i18n.Display;
 import password.pwm.i18n.Display;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.i18n.LocaleHelper;
 import password.pwm.util.java.JsonUtil;
 import password.pwm.util.java.JsonUtil;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
 
 
@@ -126,10 +126,10 @@ public class VerificationMethodValue extends AbstractValue implements StoredValu
                 }
                 }
             }
             }
 
 
-            public VerificationMethodValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey key )
+            public VerificationMethodValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey key )
                     throws PwmOperationalException
                     throws PwmOperationalException
             {
             {
-                final Element valueElement = settingElement.getChild( "value" );
+                final XmlElement valueElement = settingElement.getChild( "value" );
                 final String inputStr = valueElement.getText();
                 final String inputStr = valueElement.getText();
                 final VerificationMethodSettings settings = JsonUtil.deserialize( inputStr, VerificationMethodSettings.class );
                 final VerificationMethodSettings settings = JsonUtil.deserialize( inputStr, VerificationMethodSettings.class );
                 return new VerificationMethodValue( settings );
                 return new VerificationMethodValue( settings );
@@ -138,10 +138,10 @@ public class VerificationMethodValue extends AbstractValue implements StoredValu
     }
     }
 
 
     @Override
     @Override
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final Element valueElement = new Element( valueElementName );
-        valueElement.addContent( new CDATA( JsonUtil.serialize( value ) ) );
+        final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
+        valueElement.addText( JsonUtil.serialize( value ) );
         return Collections.singletonList( valueElement );
         return Collections.singletonList( valueElement );
     }
     }
 
 

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

@@ -22,11 +22,12 @@
 
 
 package password.pwm.config.value;
 package password.pwm.config.value;
 
 
-import org.jdom2.Element;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.StoredValue;
 import password.pwm.config.StoredValue;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.util.java.JavaHelper;
 import password.pwm.util.java.JavaHelper;
+import password.pwm.util.java.XmlElement;
+import password.pwm.util.java.XmlFactory;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.logging.PwmLogger;
 import password.pwm.util.secure.PwmHashAlgorithm;
 import password.pwm.util.secure.PwmHashAlgorithm;
 import password.pwm.util.secure.PwmSecurityKey;
 import password.pwm.util.secure.PwmSecurityKey;
@@ -54,11 +55,11 @@ public class X509CertificateValue extends AbstractValue implements StoredValue
     {
     {
         return new StoredValueFactory()
         return new StoredValueFactory()
         {
         {
-            public X509CertificateValue fromXmlElement( final PwmSetting pwmSetting, final Element settingElement, final PwmSecurityKey key )
+            public X509CertificateValue fromXmlElement( final PwmSetting pwmSetting, final XmlElement settingElement, final PwmSecurityKey key )
             {
             {
                 final List<X509Certificate> certificates = new ArrayList<>();
                 final List<X509Certificate> certificates = new ArrayList<>();
-                final List<Element> valueElements = settingElement.getChildren( "value" );
-                for ( final Element loopValueElement : valueElements )
+                final List<XmlElement> valueElements = settingElement.getChildren( "value" );
+                for ( final XmlElement loopValueElement : valueElements )
                 {
                 {
                     final String b64encodedStr = loopValueElement.getText();
                     final String b64encodedStr = loopValueElement.getText();
                     try
                     try
@@ -105,15 +106,15 @@ public class X509CertificateValue extends AbstractValue implements StoredValue
 
 
 
 
     @Override
     @Override
-    public List<Element> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
+    public List<XmlElement> toXmlValues( final String valueElementName, final PwmSecurityKey pwmSecurityKey  )
     {
     {
-        final List<Element> returnList = new ArrayList<>();
+        final List<XmlElement> returnList = new ArrayList<>();
         for ( final X509Certificate value : certificates )
         for ( final X509Certificate value : certificates )
         {
         {
-            final Element valueElement = new Element( valueElementName );
+            final XmlElement valueElement = XmlFactory.getFactory().newElement( valueElementName );
             try
             try
             {
             {
-                valueElement.addContent( X509Utils.certificateToBase64( value ) );
+                valueElement.addText( X509Utils.certificateToBase64( value ) );
             }
             }
             catch ( CertificateEncodingException e )
             catch ( CertificateEncodingException e )
             {
             {

+ 31 - 91
server/src/main/java/password/pwm/util/java/XmlUtil.java → server/src/main/java/password/pwm/util/java/LicenseInfoReader.java

@@ -23,116 +23,37 @@
 package password.pwm.util.java;
 package password.pwm.util.java;
 
 
 import lombok.Value;
 import lombok.Value;
-import org.jdom2.Document;
-import org.jdom2.Element;
-import org.jdom2.input.SAXBuilder;
-import org.jdom2.input.sax.XMLReaders;
-import org.jdom2.output.Format;
-import org.jdom2.output.XMLOutputter;
-import password.pwm.error.ErrorInformation;
-import password.pwm.error.PwmError;
 import password.pwm.error.PwmUnrecoverableException;
 import password.pwm.error.PwmUnrecoverableException;
 
 
-import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStream;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.io.Writer;
-import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Collections;
 import java.util.List;
 import java.util.List;
 
 
-public class XmlUtil
+public class LicenseInfoReader
 {
 {
-
-    public static final Charset STORAGE_CHARSET = Charset.forName( "UTF8" );
-
-    public static Document parseXml( final InputStream inputStream )
-            throws PwmUnrecoverableException
+    private LicenseInfoReader()
     {
     {
-        return parseXml( new InputStreamReader( inputStream, STORAGE_CHARSET ) );
     }
     }
 
 
-    public static Document parseXml( final Reader inputStream )
-            throws PwmUnrecoverableException
-    {
-        final SAXBuilder builder = getBuilder();
-        final Document inputDocument;
-        try
-        {
-            inputDocument = builder.build( inputStream );
-        }
-        catch ( Exception e )
-        {
-            throw new PwmUnrecoverableException( new ErrorInformation( PwmError.CONFIG_FORMAT_ERROR, null, new String[]
-                    {
-                            "error parsing xml data: " + e.getMessage(),
-                    }
-            ) );
-        }
-        return inputDocument;
-    }
-
-    public static void outputDocument( final Document document, final OutputStream outputStream )
-            throws IOException
-    {
-        final Format format = Format.getPrettyFormat();
-        format.setEncoding( STORAGE_CHARSET.toString() );
-        final XMLOutputter outputter = new XMLOutputter();
-        outputter.setFormat( format );
-
-        try ( Writer writer = new OutputStreamWriter( outputStream, STORAGE_CHARSET ) )
-        {
-            outputter.output( document, writer );
-        }
-    }
-
-    private static SAXBuilder getBuilder( )
-    {
-        final SAXBuilder builder = new SAXBuilder();
-        builder.setExpandEntities( false );
-        builder.setXMLReaderFactory( XMLReaders.NONVALIDATING );
-        builder.setFeature( "http://xml.org/sax/features/resolve-dtd-uris", false );
-        return builder;
-    }
-
-    @Value
-    public static class DependencyInfo
-    {
-        private String projectUrl;
-        private String name;
-        private String artifactId;
-        private String version;
-        private String type;
-        private List<LicenseInfo> licenses;
-    }
-
-    @Value
-    public static class LicenseInfo
-    {
-        private String licenseUrl;
-        private String licenseName;
-    }
-
-    public static List<DependencyInfo> getLicenseInfos( ) throws PwmUnrecoverableException
+    public static List<DependencyInfo> getLicenseInfos() throws PwmUnrecoverableException
     {
     {
         final List<String> attributionFiles = Arrays.asList( "/attribution.xml" );
         final List<String> attributionFiles = Arrays.asList( "/attribution.xml" );
         final List<DependencyInfo> returnList = new ArrayList<>();
         final List<DependencyInfo> returnList = new ArrayList<>();
+        final XmlFactory factory = new XmlFactory.XmlFactoryW3c();
 
 
         for ( final String attributionFile : attributionFiles )
         for ( final String attributionFile : attributionFiles )
         {
         {
-            final InputStream attributionInputStream = XmlUtil.class.getResourceAsStream( attributionFile );
+            final InputStream attributionInputStream = XmlFactory.XmlFactoryJDOM.class.getResourceAsStream( attributionFile );
 
 
             if ( attributionInputStream != null )
             if ( attributionInputStream != null )
             {
             {
-                final Document document = XmlUtil.parseXml( attributionInputStream );
-                final Element dependencies = document.getRootElement().getChild( "dependencies" );
+                final XmlDocument document = factory.parseXml( attributionInputStream );
+                final XmlElement rootElement = document.getRootElement();
+                final XmlElement dependenciesElement = rootElement.getChildren( "dependencies" ).iterator().next();
 
 
-                for ( final Element dependency : dependencies.getChildren( "dependency" ) )
+                for ( final XmlElement dependency : dependenciesElement.getChildren( "dependency" ) )
                 {
                 {
                     final String projectUrl = dependency.getChildText( "projectUrl" );
                     final String projectUrl = dependency.getChildText( "projectUrl" );
                     final String name = dependency.getChildText( "name" );
                     final String name = dependency.getChildText( "name" );
@@ -142,9 +63,9 @@ public class XmlUtil
 
 
                     final List<LicenseInfo> licenseInfos = new ArrayList<>();
                     final List<LicenseInfo> licenseInfos = new ArrayList<>();
                     {
                     {
-                        final Element licenses = dependency.getChild( "licenses" );
-                        final List<Element> licenseList = licenses.getChildren( "license" );
-                        for ( final Element license : licenseList )
+                        final XmlElement licenses = dependency.getChild( "licenses" );
+                        final List<XmlElement> licenseList = licenses.getChildren( "license" );
+                        for ( final XmlElement license : licenseList )
                         {
                         {
                             final String licenseUrl = license.getChildText( "url" );
                             final String licenseUrl = license.getChildText( "url" );
                             final String licenseName = license.getChildText( "name" );
                             final String licenseName = license.getChildText( "name" );
@@ -162,4 +83,23 @@ public class XmlUtil
         }
         }
         return Collections.unmodifiableList( returnList );
         return Collections.unmodifiableList( returnList );
     }
     }
+
+
+    @Value
+    public static class DependencyInfo
+    {
+        private String projectUrl;
+        private String name;
+        private String artifactId;
+        private String version;
+        private String type;
+        private List<LicenseInfo> licenses;
+    }
+
+    @Value
+    public static class LicenseInfo
+    {
+        private String licenseUrl;
+        private String licenseName;
+    }
 }
 }

+ 5 - 0
server/src/main/java/password/pwm/util/java/TimeDuration.java

@@ -147,6 +147,11 @@ public class TimeDuration implements Comparable, Serializable
         return ms;
         return ms;
     }
     }
 
 
+    public String asIso()
+    {
+        return this.asDuration().toString();
+    }
+
     /**
     /**
      * Create a new TimeDuration using the absolute difference as the time
      * Create a new TimeDuration using the absolute difference as the time
      * period between the two supplied timestamps.
      * period between the two supplied timestamps.

+ 150 - 0
server/src/main/java/password/pwm/util/java/XmlDocument.java

@@ -0,0 +1,150 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2018 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package password.pwm.util.java;
+
+import org.jdom2.Document;
+import org.jdom2.Element;
+import org.jdom2.filter.Filters;
+import org.jdom2.xpath.XPathExpression;
+import org.jdom2.xpath.XPathFactory;
+import org.w3c.dom.NodeList;
+
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathConstants;
+import javax.xml.xpath.XPathExpressionException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public interface XmlDocument
+{
+    XmlElement getRootElement();
+
+    XmlElement evaluateXpathToElement( String xpathExpression );
+
+    List<XmlElement> evaluateXpathToElements( String xpathExpression );
+
+    XmlDocument copy();
+
+    class XmlDocumentJDOM implements XmlDocument
+    {
+        final Document document;
+
+        XmlDocumentJDOM( final Document document )
+        {
+            this.document = document;
+        }
+
+        @Override
+        public XmlElement getRootElement()
+        {
+            return new XmlElement.XmlElementJDOM( document.getRootElement() );
+        }
+
+        @Override
+        public XmlElement evaluateXpathToElement(
+                final String xpathExpression
+        )
+        {
+            final XPathFactory xpfac = XPathFactory.instance();
+            final XPathExpression<Element> xp = xpfac.compile( xpathExpression, Filters.element() );
+            final Element element = xp.evaluateFirst( document );
+            return element == null ? null : new XmlElement.XmlElementJDOM( element );
+        }
+
+        @Override
+        public List<XmlElement> evaluateXpathToElements(
+                final String xpathExpression
+        )
+        {
+            final List<XmlElement> returnList = new ArrayList<>(  );
+
+            final XPathFactory xpfac = XPathFactory.instance();
+            final XPathExpression<Element> xp = xpfac.compile( xpathExpression, Filters.element() );
+            for ( final Element element : xp.evaluate( document ) )
+            {
+                returnList.add( new XmlElement.XmlElementJDOM( element ) );
+            }
+            return Collections.unmodifiableList( returnList );
+        }
+
+        @Override
+        public XmlDocument copy()
+        {
+            return new XmlDocumentJDOM( document.clone() );
+        }
+    }
+
+    class XmlDocumentW3c implements XmlDocument
+    {
+        final org.w3c.dom.Document document;
+
+        public XmlDocumentW3c( final org.w3c.dom.Document document )
+        {
+            this.document = document;
+        }
+
+        @Override
+        public XmlElement getRootElement()
+        {
+            return new XmlElement.XmlElementW3c( document.getDocumentElement() );
+        }
+
+        @Override
+        public XmlElement evaluateXpathToElement(
+                final String xpathExpression
+        )
+        {
+            final List<XmlElement> elements = evaluateXpathToElements( xpathExpression );
+            if ( JavaHelper.isEmpty( elements ) )
+            {
+                return null;
+            }
+            return elements.iterator().next();
+        }
+
+        @Override
+        public List<XmlElement> evaluateXpathToElements(
+                final String xpathExpression
+        )
+        {
+            try
+            {
+                final XPath xPath = javax.xml.xpath.XPathFactory.newInstance().newXPath();
+                final javax.xml.xpath.XPathExpression expression = xPath.compile( xpathExpression );
+                final NodeList nodeList = (NodeList) expression.evaluate( document, XPathConstants.NODESET );
+                return XmlFactory.XmlFactoryW3c.nodeListToElementList( nodeList );
+            }
+            catch ( XPathExpressionException e )
+            {
+                throw new IllegalStateException( "error evaluating xpath expression: " + e.getMessage() );
+            }
+        }
+
+        @Override
+        public XmlDocument copy()
+        {
+            return new XmlDocumentW3c( ( org.w3c.dom.Document) document.cloneNode( true ) );
+        }
+    }
+}

+ 396 - 0
server/src/main/java/password/pwm/util/java/XmlElement.java

@@ -0,0 +1,396 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2018 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package password.pwm.util.java;
+
+import org.jdom2.Comment;
+import org.jdom2.Content;
+import org.jdom2.Element;
+import org.jdom2.Text;
+import org.jdom2.input.DOMBuilder;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public interface XmlElement
+{
+    XmlElement getChild( String elementName );
+
+    String getAttributeValue( String attribute );
+
+    List<XmlElement> getChildren( String elementName );
+
+    String getText();
+
+    String getTextTrim();
+
+    String getChildText( String elementName );
+
+    org.jdom2.Element asJdomElement();
+
+    String getName();
+
+    void setAttribute( String name, String value );
+
+    void detach();
+
+    void removeContent();
+
+    void removeAttribute( String attributeName );
+
+    void addContent( XmlElement element );
+
+    void addContent( List<XmlElement> elements );
+
+    void addText( String text );
+
+    void setComment( List<String> textLines );
+
+    List<XmlElement> getChildren();
+
+    class XmlElementJDOM implements XmlElement
+    {
+        private final Element element;
+
+        XmlElementJDOM( final Element element )
+        {
+            this.element = element;
+        }
+
+        @Override
+        public String getName()
+        {
+            return element.getName();
+        }
+
+        @Override
+        public XmlElement getChild( final String elementName )
+        {
+            final List<XmlElement> children = getChildren( elementName );
+            if ( JavaHelper.isEmpty( children ) )
+            {
+                return null;
+            }
+            return children.iterator().next();
+        }
+
+        @Override
+        public String getAttributeValue( final String attribute )
+        {
+            return element.getAttributeValue( attribute );
+        }
+
+        @Override
+        public List<XmlElement> getChildren()
+        {
+            return getChildren( null );
+        }
+
+        @Override
+        public List<XmlElement> getChildren( final String elementName )
+        {
+
+            final List<Element> children = elementName == null
+                    ? element.getChildren()
+                    : element.getChildren( elementName );
+            if ( children == null )
+            {
+                return Collections.emptyList();
+            }
+            final List<XmlElement> xmlElements = new ArrayList<>();
+            for ( final Element element : children )
+            {
+                xmlElements.add( new XmlElementJDOM( element ) );
+            }
+            return xmlElements;
+        }
+
+        @Override
+        public String getText()
+        {
+            return element.getText();
+        }
+
+        @Override
+        public String getTextTrim()
+        {
+            return element.getTextTrim();
+        }
+
+        @Override
+        public String getChildText( final String elementName )
+        {
+            final XmlElement child = getChild( elementName );
+            if ( child == null )
+            {
+                return null;
+            }
+            return child.getText();
+        }
+
+        @Override
+        public Element asJdomElement()
+        {
+            return element;
+        }
+
+        @Override
+        public void setAttribute( final String name, final String value )
+        {
+            element.setAttribute( name, value );
+        }
+
+        @Override
+        public void detach()
+        {
+            element.detach();
+        }
+
+        @Override
+        public void removeContent()
+        {
+            element.removeContent();
+        }
+
+        @Override
+        public void removeAttribute( final String attributeName )
+        {
+            element.removeAttribute( attributeName );
+        }
+
+        @Override
+        public void addContent( final XmlElement element )
+        {
+            this.element.addContent( ( ( XmlElementJDOM) element ).element );
+        }
+
+        public void addContent( final List<XmlElement> elements )
+        {
+            for ( final XmlElement loopElement : elements )
+            {
+                final Element jdomElement = ( ( XmlElementJDOM ) loopElement ).element;
+                this.element.addContent( jdomElement );
+            }
+        }
+
+        @Override
+        public void addText( final String text )
+        {
+            element.addContent( new Text( text ) );
+        }
+
+        @Override
+        public void setComment( final List<String> textLines )
+        {
+            final List<Content> contentList = new ArrayList<>( element.getContent() );
+            for ( final Content content : contentList )
+            {
+                if ( content instanceof Comment )
+                {
+                    content.detach();
+                }
+            }
+
+            final List<String> reversedList = new ArrayList<>( textLines );
+            Collections.reverse( reversedList );
+            for ( final String text : textLines )
+            {
+                element.addContent( 0, new Comment( text ) );
+            }
+        }
+    }
+
+    class XmlElementW3c implements XmlElement
+    {
+        private final org.w3c.dom.Element element;
+
+        XmlElementW3c( final org.w3c.dom.Element element )
+        {
+            this.element = element;
+        }
+
+        @Override
+        public String getName()
+        {
+            return element.getTagName();
+        }
+
+        @Override
+        public XmlElement getChild( final String elementName )
+        {
+            final List<XmlElement> children = getChildren( elementName );
+            if ( JavaHelper.isEmpty( children ) )
+            {
+                return null;
+            }
+            return children.iterator().next();
+        }
+
+        @Override
+        public String getAttributeValue( final String attribute )
+        {
+            final String attrValue = element.getAttribute( attribute );
+            return StringUtil.isEmpty( attrValue ) ? null : attrValue;
+        }
+
+        @Override
+        public List<XmlElement> getChildren()
+        {
+            final NodeList nodeList = element.getChildNodes();
+            return XmlFactory.XmlFactoryW3c.nodeListToElementList( nodeList );
+        }
+
+        @Override
+        public List<XmlElement> getChildren( final String elementName )
+        {
+            final NodeList nodeList = element.getElementsByTagName( elementName );
+            return XmlFactory.XmlFactoryW3c.nodeListToElementList( nodeList );
+        }
+
+        @Override
+        public String getText()
+        {
+            final String value = element.getTextContent();
+            return StringUtil.isEmpty( value ) ? null : value;
+        }
+
+        @Override
+        public String getTextTrim()
+        {
+            final String result = element.getTextContent();
+            return result == null ? null : result.trim();
+        }
+
+        @Override
+        public String getChildText( final String elementName )
+        {
+            final XmlElement child = getChild( elementName );
+            if ( child == null )
+            {
+                return null;
+            }
+            return child.getText();
+        }
+
+        @Override
+        public Element asJdomElement()
+        {
+            final DOMBuilder domBuilder = new DOMBuilder();
+            return domBuilder.build( element );
+        }
+
+        @Override
+        public void setAttribute( final String name, final String value )
+        {
+            element.setAttribute( name, value );
+        }
+
+        @Override
+        public void detach()
+        {
+            element.getParentNode().removeChild( element );
+        }
+
+        @Override
+        public void removeContent()
+        {
+            final NodeList nodeList = element.getChildNodes();
+            for ( final XmlElement child : XmlFactory.XmlFactoryW3c.nodeListToElementList( nodeList ) )
+            {
+                element.removeChild( ( (XmlElementW3c) child ).element );
+            }
+        }
+
+        @Override
+        public void removeAttribute( final String attributeName )
+        {
+            element.removeAttribute( attributeName );
+        }
+
+        @Override
+        public void addContent( final XmlElement element )
+        {
+            final org.w3c.dom.Element w3cElement = ( ( XmlElementW3c ) element ).element;
+            this.element.getOwnerDocument().adoptNode( w3cElement );
+            this.element.appendChild( w3cElement );
+        }
+
+        public void addContent( final List<XmlElement> elements )
+        {
+            for ( final XmlElement element : elements )
+            {
+                final org.w3c.dom.Element w3cElement = ( ( XmlElementW3c ) element ).element;
+                this.element.getOwnerDocument().adoptNode( w3cElement );
+                this.element.appendChild( w3cElement );
+            }
+        }
+
+        @Override
+        public void addText( final String text )
+        {
+            final DocumentBuilder documentBuilder = XmlFactory.XmlFactoryW3c.getBuilder();
+            final org.w3c.dom.Document document = documentBuilder.newDocument();
+            final org.w3c.dom.Text textNode = document.createTextNode( text );
+            this.element.getOwnerDocument().adoptNode( textNode );
+            element.appendChild( textNode );
+        }
+
+        @Override
+        public void setComment( final List<String> textLines )
+        {
+            final NodeList nodeList = element.getChildNodes();
+            for ( int i = 0; i < nodeList.getLength(); i++ )
+            {
+                final Node node = nodeList.item( i );
+                if ( node.getNodeType() == Node.COMMENT_NODE )
+                {
+                    element.removeChild( node );
+                }
+            }
+
+            final DocumentBuilder documentBuilder = XmlFactory.XmlFactoryW3c.getBuilder();
+            final org.w3c.dom.Document document = documentBuilder.newDocument();
+
+            final List<String> reversedList = new ArrayList<>( textLines );
+            Collections.reverse( reversedList );
+            for ( final String text : reversedList )
+            {
+                final org.w3c.dom.Comment textNode = document.createComment( text );
+                this.element.getOwnerDocument().adoptNode( textNode );
+
+                if ( element.hasChildNodes() )
+                {
+                    element.insertBefore( textNode, element.getFirstChild() );
+                }
+                else
+                {
+                    element.appendChild( textNode );
+                }
+
+            }
+        }
+    }
+}

+ 252 - 0
server/src/main/java/password/pwm/util/java/XmlFactory.java

@@ -0,0 +1,252 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2018 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package password.pwm.util.java;
+
+import org.jdom2.Document;
+import org.jdom2.input.SAXBuilder;
+import org.jdom2.input.sax.XMLReaders;
+import org.jdom2.output.Format;
+import org.jdom2.output.XMLOutputter;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+import password.pwm.error.ErrorInformation;
+import password.pwm.error.PwmError;
+import password.pwm.error.PwmUnrecoverableException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.List;
+
+public interface XmlFactory
+{
+    XmlDocument parseXml( InputStream inputStream )
+            throws PwmUnrecoverableException;
+
+    void outputDocument( XmlDocument document, OutputStream outputStream )
+                    throws IOException;
+
+    XmlDocument newDocument( String rootElementName );
+
+    XmlElement newElement( String name );
+
+    static XmlFactory getFactory()
+    {
+        //return new XmlFactoryW3c();
+        return new XmlFactoryJDOM();
+    }
+
+    class XmlFactoryJDOM implements XmlFactory
+    {
+        private static final Charset STORAGE_CHARSET = Charset.forName( "UTF8" );
+
+        XmlFactoryJDOM()
+        {
+        }
+
+        @Override
+        public XmlDocument parseXml( final InputStream inputStream )
+                throws PwmUnrecoverableException
+        {
+            final SAXBuilder builder = getBuilder();
+            final Document inputDocument;
+            try
+            {
+                inputDocument = builder.build( inputStream );
+            }
+            catch ( Exception e )
+            {
+                throw new PwmUnrecoverableException( new ErrorInformation( PwmError.CONFIG_FORMAT_ERROR, null, new String[]
+                        {
+                                "error parsing xml data: " + e.getMessage(),
+                        }
+                ) );
+            }
+            return new XmlDocument.XmlDocumentJDOM( inputDocument );
+        }
+
+        public static void outputJDOMDocument( final Document document, final OutputStream outputStream )
+                throws IOException
+        {
+            new XmlFactoryJDOM().outputDocument( document, outputStream );
+        }
+
+        @Override
+        public void outputDocument( final XmlDocument document, final OutputStream outputStream )
+                throws IOException
+        {
+            final Document jdomDoc =  ( ( XmlDocument.XmlDocumentJDOM )  document ).document;
+            this.outputDocument( jdomDoc, outputStream );
+        }
+
+        private void outputDocument( final Document document, final OutputStream outputStream )
+                throws IOException
+        {
+            final Format format = Format.getPrettyFormat();
+            format.setEncoding( STORAGE_CHARSET.toString() );
+            final XMLOutputter outputter = new XMLOutputter();
+            outputter.setFormat( format );
+
+            try ( Writer writer = new OutputStreamWriter( outputStream, STORAGE_CHARSET ) )
+            {
+                outputter.output( document, writer );
+            }
+        }
+
+        private static SAXBuilder getBuilder( )
+        {
+            final SAXBuilder builder = new SAXBuilder();
+            builder.setExpandEntities( false );
+            builder.setXMLReaderFactory( XMLReaders.NONVALIDATING );
+            builder.setFeature( "http://xml.org/sax/features/resolve-dtd-uris", false );
+            return builder;
+        }
+
+        @Override
+        public XmlDocument newDocument( final String rootElementName )
+        {
+            final org.jdom2.Element rootElement = new org.jdom2.Element( rootElementName );
+            final org.jdom2.Document newDoc = new org.jdom2.Document( rootElement );
+            return new XmlDocument.XmlDocumentJDOM( newDoc );
+        }
+
+        @Override
+        public XmlElement newElement( final String name )
+        {
+            return new XmlElement.XmlElementJDOM( new org.jdom2.Element ( name ) );
+        }
+    }
+
+    class XmlFactoryW3c implements XmlFactory
+    {
+        private static final Charset STORAGE_CHARSET = Charset.forName( "UTF8" );
+
+        XmlFactoryW3c()
+        {
+        }
+
+        @Override
+        public XmlDocument parseXml( final InputStream inputStream )
+                throws PwmUnrecoverableException
+        {
+            final org.w3c.dom.Document inputDocument;
+            try
+            {
+                final DocumentBuilder builder = getBuilder();
+                inputDocument = builder.parse( inputStream );
+            }
+            catch ( Exception e )
+            {
+                throw new PwmUnrecoverableException( new ErrorInformation( PwmError.CONFIG_FORMAT_ERROR, null, new String[]
+                        {
+                                "error parsing xml data: " + e.getMessage(),
+                        }
+                ) );
+            }
+            return new XmlDocument.XmlDocumentW3c( inputDocument );
+        }
+
+        static DocumentBuilder getBuilder()
+        {
+            try
+            {
+                final DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
+                dbFactory.setFeature( "http://apache.org/xml/features/disallow-doctype-decl", false );
+                dbFactory.setExpandEntityReferences( false );
+                dbFactory.setValidating( false );
+                dbFactory.setXIncludeAware( false );
+                dbFactory.setExpandEntityReferences( false );
+                return dbFactory.newDocumentBuilder();
+            }
+            catch ( ParserConfigurationException e )
+            {
+                throw new IllegalArgumentException( "unable to generate dom xml builder: " + e.getMessage() );
+            }
+        }
+
+        @Override
+        public void outputDocument( final XmlDocument document, final OutputStream outputStream )
+                throws IOException
+        {
+            try
+            {
+                final Transformer tr = TransformerFactory.newInstance().newTransformer();
+                tr.setOutputProperty( OutputKeys.INDENT, "yes" );
+                tr.setOutputProperty( OutputKeys.METHOD, "xml" );
+                tr.setOutputProperty( OutputKeys.ENCODING, STORAGE_CHARSET.toString() );
+                tr.transform( new DOMSource( ( ( XmlDocument.XmlDocumentW3c ) document ).document ), new StreamResult( outputStream ) );
+            }
+            catch ( TransformerException e )
+            {
+                throw new IOException( "error loading xml transformer: " + e.getMessage() );
+            }
+        }
+
+        static List<XmlElement> nodeListToElementList( final NodeList nodeList )
+        {
+            final List<XmlElement> returnList = new ArrayList<>();
+            if ( nodeList != null )
+            {
+                for ( int i = 0; i < nodeList.getLength(); i++ )
+                {
+                    returnList.add( new XmlElement.XmlElementW3c( ( Element ) nodeList.item( i ) ) );
+                }
+                return returnList;
+            }
+            return null;
+        }
+
+        @Override
+        public XmlDocument newDocument( final String rootElementName )
+        {
+            final DocumentBuilder documentBuilder = getBuilder();
+            final org.w3c.dom.Document document = documentBuilder.newDocument();
+            final org.w3c.dom.Element rootElement = document.createElement( rootElementName );
+            document.appendChild( rootElement );
+            return new XmlDocument.XmlDocumentW3c( document );
+        }
+
+        @Override
+        public XmlElement newElement( final String name )
+        {
+            final DocumentBuilder documentBuilder = getBuilder();
+            final org.w3c.dom.Document document = documentBuilder.newDocument();
+            final org.w3c.dom.Element element = document.createElement( name );
+            return new XmlElement.XmlElementW3c( element );
+        }
+
+    }
+}

+ 46 - 0
server/src/test/java/password/pwm/util/java/XmlFactoryTest.java

@@ -0,0 +1,46 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2018 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package password.pwm.util.java;
+
+import org.junit.Assert;
+import org.junit.Test;
+import password.pwm.error.PwmUnrecoverableException;
+
+import java.io.InputStream;
+import java.util.List;
+
+public class XmlFactoryTest
+{
+    @Test
+    public void testLoadXml()
+            throws PwmUnrecoverableException
+    {
+        final InputStream xmlFactoryTestXmlFile = this.getClass().getResourceAsStream( "XmlFactoryTest.xml" );
+        final XmlDocument xmlDocument = XmlFactory.getFactory().parseXml( xmlFactoryTestXmlFile );
+        Assert.assertEquals( "PwmConfiguration", xmlDocument.getRootElement().getName() );
+        final XmlElement configIsEditable = xmlDocument.evaluateXpathToElement( "//property[@key='configIsEditable']" );
+        Assert.assertEquals( "false", configIsEditable.getText() );
+        final List<XmlElement> allSettings = xmlDocument.evaluateXpathToElements( "//setting" );
+        Assert.assertEquals( 280, allSettings.size() );
+    }
+}

File diff suppressed because it is too large
+ 84 - 0
server/src/test/resources/password/pwm/util/java/XmlFactoryTest.xml


+ 6 - 11
webapp/src/main/webapp/public/reference/license.jsp

@@ -20,15 +20,10 @@
  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 --%>
 --%>
 
 
-<%@ page import="password.pwm.http.JspUtility" %>
-<%@ page import="password.pwm.util.java.XmlUtil" %>
-<%@ page import="org.jdom2.Document" %>
-<%@ page import="org.jdom2.Element" %>
-<%@ page import="java.util.List" %>
-<%@ page import="java.io.InputStream" %>
-<%@ page import="org.apache.commons.lang3.StringUtils" %>
-<%@ page import="password.pwm.util.java.StringUtil" %>
 <%@ page import="password.pwm.Permission" %>
 <%@ page import="password.pwm.Permission" %>
+<%@ page import="password.pwm.util.java.StringUtil" %>
+<%@ page import="java.util.List" %>
+<%@ page import="password.pwm.util.java.LicenseInfoReader" %>
 
 
 <!DOCTYPE html>
 <!DOCTYPE html>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
 <%@ page language="java" session="true" isThreadSafe="true" contentType="text/html" %>
@@ -43,7 +38,7 @@
 <%@ include file="/WEB-INF/jsp/fragment/header.jsp" %>
 <%@ include file="/WEB-INF/jsp/fragment/header.jsp" %>
 <body class="nihilo">
 <body class="nihilo">
 <link href="<pwm:url url='/public/resources/referenceStyle.css' addContext="true"/>" rel="stylesheet" type="text/css"/>
 <link href="<pwm:url url='/public/resources/referenceStyle.css' addContext="true"/>" rel="stylesheet" type="text/css"/>
-<% List<XmlUtil.DependencyInfo> dependencyInfos = XmlUtil.getLicenseInfos(); %>
+<% List<LicenseInfoReader.DependencyInfo> dependencyInfos = LicenseInfoReader.getLicenseInfos(); %>
 <div id="wrapper">
 <div id="wrapper">
     <jsp:include page="../../WEB-INF/jsp/fragment/header-body.jsp">
     <jsp:include page="../../WEB-INF/jsp/fragment/header-body.jsp">
         <jsp:param name="pwm.PageName" value="Software License Reference"/>
         <jsp:param name="pwm.PageName" value="Software License Reference"/>
@@ -61,7 +56,7 @@
         </pwm:if>
         </pwm:if>
 
 
         <% if (dependencyInfos != null) { %>
         <% if (dependencyInfos != null) { %>
-        <% for (final XmlUtil.DependencyInfo dependencyInfo : dependencyInfos) { %>
+        <% for (final LicenseInfoReader.DependencyInfo dependencyInfo : dependencyInfos) { %>
         <div class="licenseBlock">
         <div class="licenseBlock">
             <div class="dependency-name"><%=StringUtil.escapeHtml(dependencyInfo.getName())%></div>
             <div class="dependency-name"><%=StringUtil.escapeHtml(dependencyInfo.getName())%></div>
 
 
@@ -80,7 +75,7 @@
                 </div>
                 </div>
             </pwm:if>
             </pwm:if>
             <%
             <%
-                for (final XmlUtil.LicenseInfo licenseInfo : dependencyInfo.getLicenses()) { %>
+                for (final LicenseInfoReader.LicenseInfo licenseInfo : dependencyInfo.getLicenses()) { %>
             <div class="dependency-license">
             <div class="dependency-license">
                 License:
                 License:
                 <a href="<%=licenseInfo.getLicenseUrl()%>" target="_blank" class="license-link">
                 <a href="<%=licenseInfo.getLicenseUrl()%>" target="_blank" class="license-link">

Some files were not shown because too many files changed in this diff