Version 2

    Within JBoss AS7 all of the configuration that can be written for standalone and domain server is described by making use of XML schemas, the XML is then parsed using parsers making StAX calls.

     

    This approach has started to lead to possible issues in a couple of areas: -

    • Error checking / reporting is duplicated across all of the parsers.
    • No automated validation realistically possible that the parser implementation matches the schema definition.

     

    This article is to propose a possible wrapper around the existing XMLElementReader that at server runtime takes over the error reporting during parsing, this in turn will open up possibilites within the testsuite to validate that the calls the parser makes to the new wrapper match the definition of the schema.

     

    The change to the wrapper would potentially be intrusive as code making calls to the XMLElementReader API and performing validation would need to change to make calls to the new wrapper and remove the validation - having said that the wrapper is after all a wrapper, this means that integration could be gradual e.g. it could be introduced on a subsystem by subsystem basis as a subsystem moves to a new namespace version.

    Runtime

    The proposal is that a wrapper around the XMLElementReader that implements the following interface will be provided: -

     

    public interface ValidatingXmlReader {
    
        /**
         * Identify if the next element is the element specified by 'name'
         *
         * @param name - the name to compare with the name of the next element.
         * @param optional - is the next element optional.
         * @throws UnexpectedAttributeException - if there are still attributes that have not been read.
         * @throws UnexpectedElementException - if optional is false and the next
         *                                      element does not match the name supplied.
         * @throws UnexpectedContent - if the wrapping element had content that has not been read.
         * @throws MissingElementException - if optional is false and the end element is encountered.
         *
         * @return true if the name of the next element matches the name supplied, otherwise false.
         */
        boolean nextElement(String name, boolean optional);
    
        /**
         * Return the content of the element.
         *
         * @param optional is content optional on the current element.
         * @throws UnexpectedAttributeException - if there are still attributes that have not been read.
         *
         * @return the content of the element or null if it is optional and not specified.
         */
        String readElementContent(boolean optional);
    
        /**
         * Return the value of the specified attribute.
         *
         * @param name - the name of the attribute to read.
         * @param optional - is this an optional attribute?
         * @throws MissingAttributeException - if the element is not optional and it is not specified.
         * @return the value of the attribute or null if it is optional and not specified.
         */
        String readAttribute(String name, boolean optional);
    
        /**
         * Verifies that an end element has been reached.
         *
         * @throws UnexpectedAttributeException - if there are still attributes that have not been read.
         * @throws UnexpectedElementException - if optional is false and the next
         *                                      element does not match the name supplied.
         * @throws UnexpectedContent - if the element had content that has not been read.
         */
        void endElement();
    
    }
    

     

    The following (modified) complex type definition is for the host element in the host.xml configuration file: -

     

        <xs:element name='host'>
            <xs:complexType>
                <xs:sequence>
                    <xs:element name='system-properties' type='properties-with-boottime' minOccurs='0'/>
                    <xs:element name='paths' type='specified-pathsType' minOccurs='0' maxOccurs='1' />
                    <xs:element name='management' type='managementType' minOccurs='0' maxOccurs='1'/>
                    <xs:element name='domain-controller' type='domain-controllerType'/>
                    <xs:element name='interfaces' type='specified-interfacesType' minOccurs='0' maxOccurs='unbounded'/>
                </xs:sequence>
                <xs:attribute name='name' type='xs:string' use='optional'/>
            </xs:complexType>
        </xs:element>
    
    

     

    Using the new wrapper to read an XML document containing this type would be as: -

     

        private void readHostElement(final ValidatingXmlReader reader, final ModelNode address, final List list) {
            String name = reader.readAttribute("name", true);
            if (reader.nextElement("system-properties", true)) {
                readSystemProperties(reader, address, list);
            }
            if (reader.nextElement("paths", true)) {
                readPaths(reader, address, list);
            }
            if (reader.nextElement("management", true)) {
                readManagement(reader, address, list);
            }
            if (reader.nextElement("domain-controller", false)) {
                readDomainController(reader, address, list);
            }
            while (reader.nextElement("interfaces", true)) {
                readInterfaces(reader, address, list);
            }
            reader.endElement();
        }
    
    

    So the call structure still follows the same structure we are already using, the main difference is that apart from the call to end element to signify that we have reached the end there is now no element / attribute checking or error handling - the wrapper handles all of that now by keeping up with the calls from the parser.

     

    Here are a few possible errors that could have been reported here: -

    • If an additional attribute was provided an exception would have been thrown from the call to nextElement for "system-properties"
    • If an additional "paths" element was supplied an exception would have been called from the call to nextElement for "domain-controller"
    • If the "domain-controller" element was missing the call to nextElement for "domain-controller" would thrown an exception.
    • If an additional "domain-controler" element was provided the call to endElement would throw an exception.

     

     

    Testsuite

    Note: The testsuite enhancement introduces schema validation, this is an overhead however it only applies during specific tests - everything else will just be using the wrapper with no notion of the underlying schema.

     

    As the change requires the parsers to specify the elements and attributes expected and identify if the requested items are optional when running within the testsuite it would also be potentially possible to verify those calls against the schema definition, this would allow the following scenarios to be detected within the testsuite: -

     

    • Missed calls for seldom used optional elements / attributes.
    • Calls for elements / attributes not specified in the schema.
    • Missed calls for subsequent instances of elements where more than one can be supplied.
    • Calls for additional instances of elements beyond the limits specified in the schema.

     

    For testing within the testsuite the first pre-requisit to resolve will be obtaining a representation of the schema at least to the point of fully modelling the structure of the schema.

     

    For test cases the idea would be to perform the validated testing in issolation within the testsuite, the test would be to load configuration (or configuration extracts for subsystems) that uses all of the possible XML structure - the resulting operations will not be executed against a server so the test is just that the entire schema is supported and results in ModelNode operations.

     

    Similar to code coverage tools the validating wrapper can monitor how much of the schema is actually visited during testing, a report could be generated of areas not visited and even a pre-determined threshold set for how much of schema to cover automatically failing the test if the threshold is not met - this could provide a smoke test that automatically fails if a new XML element is added but not visited during testing.

     

    Limitations

    There are a couple of limitations in testing: -

    • Where there is an XML choice in a schema multiple XML representations will be needed to ensure all choices are tested.
    • Where an element is unbounded the wrapper can validate that there was at lease one optional call for the element after the last instance encountered, however it can not validate how long the parser would keep looping  for true unbounded.