Explorar el Código

xml factory and wrapper classes

jrivard@gmail.com hace 6 años
padre
commit
a520176347

+ 36 - 37
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.LocaleHelper;
 import password.pwm.util.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;
 
 
@@ -1270,18 +1269,18 @@ 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.asJdomElement(), null );
                     returnObj.add( new TemplateSetAssociation( storedValue, definedTemplates ) );
                     returnObj.add( new TemplateSetAssociation( storedValue, definedTemplates ) );
                 }
                 }
             }
             }
@@ -1319,20 +1318,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() );
                     }
                     }
                 }
                 }
             }
             }
@@ -1348,14 +1347,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 )
                         {
                         {
@@ -1366,7 +1365,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() );
                     }
                     }
                 }
                 }
             }
             }
@@ -1382,9 +1381,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();
 
 
@@ -1409,12 +1408,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,
@@ -1460,9 +1459,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() );
@@ -1483,9 +1482,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();
@@ -1495,9 +1494,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();
@@ -1507,9 +1506,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();
@@ -1519,8 +1518,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.LocaleHelper;
 import password.pwm.util.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;
 
 
 public enum PwmSettingTemplate
 public enum PwmSettingTemplate
 {
 {
@@ -62,14 +61,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 )
         {
         {

+ 1 - 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" );
@@ -176,7 +171,7 @@ public class ConfigurationReader
                     {
                     {
                             errorMsg,
                             errorMsg,
                     }
                     }
-                    );
+            );
             this.configMode = PwmApplicationMode.ERROR;
             this.configMode = PwmApplicationMode.ERROR;
             throw new PwmUnrecoverableException( errorInformation );
             throw new PwmUnrecoverableException( errorInformation );
         }
         }

+ 13 - 5
server/src/main/java/password/pwm/config/stored/NGStoredConfigurationFactory.java

@@ -31,7 +31,7 @@ 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.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,7 +66,7 @@ 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 Document inputDocument = XmlFactory.XmlFactoryJDOM.parseJDOMXml( inputStream );
             final Element rootElement = inputDocument.getRootElement();
             final Element rootElement = inputDocument.getRootElement();
 
 
             final PwmSecurityKey pwmSecurityKey = readSecurityKey( rootElement );
             final PwmSecurityKey pwmSecurityKey = readSecurityKey( rootElement );
@@ -82,7 +82,7 @@ public class NGStoredConfigurationFactory
                         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 );
                 }
                 }
@@ -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;
 
 
@@ -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 Element defaultElement = settingElement.getChild( StoredConfiguration.XML_ELEMENT_DEFAULT );
+                if ( defaultElement != null )
+                {
+                    return null;
+                }
+
                 {
                 {
                     try
                     try
                     {
                     {

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

@@ -60,7 +60,7 @@ 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.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;
@@ -128,7 +128,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 Document inputDocument = XmlFactory.XmlFactoryJDOM.parseJDOMXml( xmlData );
         final StoredConfigurationImpl newConfiguration = StoredConfigurationImpl.newStoredConfiguration();
         final StoredConfigurationImpl newConfiguration = StoredConfigurationImpl.newStoredConfiguration();
 
 
         try
         try
@@ -568,7 +568,7 @@ public class StoredConfigurationImpl implements StoredConfiguration
             throws IOException, PwmUnrecoverableException
             throws IOException, PwmUnrecoverableException
     {
     {
         ConfigurationCleaner.updateMandatoryElements( document );
         ConfigurationCleaner.updateMandatoryElements( document );
-        XmlUtil.outputDocument( document, outputStream );
+        XmlFactory.XmlFactoryJDOM.outputJDOMDocument( document, outputStream );
     }
     }
 
 
     public List<String> profilesForSetting( final PwmSetting pwmSetting )
     public List<String> profilesForSetting( final PwmSetting pwmSetting )

+ 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;
-    }
 }
 }

+ 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( "/server-attribution.xml", "/webapp-attribution.xml" );
         final List<String> attributionFiles = Arrays.asList( "/server-attribution.xml", "/webapp-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.

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

@@ -0,0 +1,109 @@
+/*
+ * 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.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.List;
+
+public interface XmlDocument
+{
+    XmlElement getRootElement();
+
+    XmlElement evaluateXpathToElement( String xpathExpression );
+
+    class XmlDocumentJDOM implements XmlDocument
+    {
+        final Document document;
+
+        public 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<Object> xp = xpfac.compile( xpathExpression );
+            final Element settingElement = ( Element ) xp.evaluateFirst( document );
+            return settingElement == null ? null : new XmlElement.XmlElementJDOM( settingElement );
+        }
+
+    }
+
+    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
+        )
+        {
+            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 );
+                final List<XmlElement> elementList = XmlFactory.XmlFactoryW3c.nodeListToElementList( nodeList );
+                if ( JavaHelper.isEmpty( elementList ) )
+                {
+                    return null;
+                }
+                return elementList.iterator().next();
+            }
+            catch ( XPathExpressionException e )
+            {
+                throw new IllegalStateException( "error evaluating xpath expression: " + e.getMessage() );
+            }
+        }
+
+    }
+}

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

@@ -0,0 +1,187 @@
+/*
+ * 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.Element;
+import org.jdom2.input.DOMBuilder;
+import org.w3c.dom.NodeList;
+
+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();
+
+    class XmlElementJDOM implements XmlElement
+    {
+        private final Element element;
+
+        XmlElementJDOM( final Element element )
+        {
+            this.element = element;
+        }
+
+        @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( final String elementName )
+        {
+            final List<Element> children = 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;
+        }
+    }
+
+    class XmlElementW3c implements XmlElement
+    {
+        private final org.w3c.dom.Element element;
+
+        XmlElementW3c( final org.w3c.dom.Element element )
+        {
+            this.element = element;
+        }
+
+        @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 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 );
+        }
+    }
+}

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

@@ -0,0 +1,218 @@
+/*
+ * 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;
+
+    static XmlFactory getFactory()
+    {
+        return new XmlFactoryJDOM();
+    }
+
+    class XmlFactoryJDOM implements XmlFactory
+    {
+        private static final Charset STORAGE_CHARSET = Charset.forName( "UTF8" );
+
+        XmlFactoryJDOM()
+        {
+        }
+
+        public static Document parseJDOMXml( final InputStream inputStream )
+                throws PwmUnrecoverableException
+        {
+            final XmlDocument xmlDocument = new XmlFactoryJDOM().parseXml( inputStream );
+                return ( ( XmlDocument.XmlDocumentJDOM ) xmlDocument ).document;
+        }
+
+        @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;
+        }
+
+    }
+
+    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 );
+        }
+
+        private static DocumentBuilder getBuilder( )
+                throws ParserConfigurationException
+        {
+            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();
+        }
+
+        @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;
+        }
+
+
+    }
+}

+ 5 - 4
server/src/test/java/password/pwm/tests/PwmPasswordJudgeTest.java

@@ -22,19 +22,20 @@
 
 
 package password.pwm.tests;
 package password.pwm.tests;
 
 
-import junit.framework.Assert;
-import junit.framework.TestCase;
+import org.junit.Assert;
+import org.junit.Test;
 import org.mockito.Mockito;
 import org.mockito.Mockito;
 import password.pwm.config.Configuration;
 import password.pwm.config.Configuration;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.PwmSetting;
 import password.pwm.config.option.StrengthMeterType;
 import password.pwm.config.option.StrengthMeterType;
-import password.pwm.util.java.JavaHelper;
 import password.pwm.util.operations.PasswordUtility;
 import password.pwm.util.operations.PasswordUtility;
 
 
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 
 
-public class PwmPasswordJudgeTest extends TestCase {
+public class PwmPasswordJudgeTest
+{
+    @Test
     public void testJudgePassword() throws Exception {
     public void testJudgePassword() throws Exception {
         final Configuration configuration = Mockito.mock(Configuration.class);
         final Configuration configuration = Mockito.mock(Configuration.class);
         Mockito.when(configuration.readSettingAsEnum(PwmSetting.PASSWORD_STRENGTH_METER_TYPE, StrengthMeterType.class)).thenReturn(StrengthMeterType.PWM);
         Mockito.when(configuration.readSettingAsEnum(PwmSetting.PASSWORD_STRENGTH_METER_TYPE, StrengthMeterType.class)).thenReturn(StrengthMeterType.PWM);

+ 33 - 0
server/src/test/java/password/pwm/util/XmlElementTest.java

@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+import org.junit.Test;
+
+public class XmlElementTest
+{
+    @Test
+    public void testLoadXml()
+    {
+
+    }
+}

+ 2 - 1
server/src/test/java/password/pwm/util/queue/EmailQueueManagerTest.java

@@ -22,8 +22,8 @@
 
 
 package password.pwm.util.queue;
 package password.pwm.util.queue;
 
 
-import junit.framework.Assert;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.io.IOUtils;
+import org.junit.Assert;
 import org.junit.Test;
 import org.junit.Test;
 import org.mockito.Mockito;
 import org.mockito.Mockito;
 import password.pwm.AppProperty;
 import password.pwm.AppProperty;
@@ -41,6 +41,7 @@ import java.util.List;
 import java.util.Properties;
 import java.util.Properties;
 
 
 public class EmailQueueManagerTest {
 public class EmailQueueManagerTest {
+
     @Test
     @Test
     public void testConvertEmailItemToMessage() throws MessagingException, IOException {
     public void testConvertEmailItemToMessage() throws MessagingException, IOException {
         EmailService emailService = new EmailService();
         EmailService emailService = new EmailService();

+ 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">