Skip to content

validation_system

Bill Majurski edited this page Jun 21, 2016 · 1 revision

Validation System

The current validation system in toolkit has many weak points. This article documents a proposed new architecture for validation components.

Goals

Use the same execution engine we have now. Modifications are acceptable.

Current validators and new-style validators must run together in the (updated) validation engine. This allows a gradual shift.

Assertions are first class objects so that tools can be written to analyse and manipulate them. A first class object in Java/Groovy is the Class so an assertion is defined as a class.

There are multiple validation contexts. They must all be dealt with. Some are: Metadata class, Assertion class (test engine).

Branching must be accommodated. By branching I mean that the execution of an assertion updates the validation context thus determining what validation steps are executed. An example is a message received by the proxy. The inputs are HTTP header and HTTP body. Once the header is examined it can be decide whether SIMPLE SOAP model or MTOM model is executed. In some cases the validation request will include information on what transaction type is expected. In other cases not.

Architecture

An assertion is a class (Java or Groovy). Assertions are grouped into bundles by the Java package they belong in. As a result the assertion database is a Java Classpath. The Java Reflection API is used to scan the assertion database. Assertion bundles are grouped in other bundles. This grouping is done to help organize for the editor the assertions. It plays no role in the order of execution or the selection for execution.

An assertion has metadata associated with it. Since an assertion is a class, the metadata is accessible via a getter.

An assertion group/bundle has metadata associated with it. Since an assertion group/bundle is a Java package, the metadata is defined by / held in a class. This special class is identified by its implementing a marker interface. Group metadata is optional. There will be defaults for all settings.

Assertion group metadata

Assertion group metadata may contain:

  • Order of execution of assertions in the group. (List) This is a gettable list of class objects (MyClass.class in Java). If this list does not exist or is empty then the assertions are executed in arbitrary order. Assertions defined in the group that are not included in the order of execution list are executed in arbitrary order after the listed assertions.

  • Actor, transaction, option, assertion identification. A child group inherits and may further constrain this identification.

  • Inherited assertion groups

Assertion metadata

Assertion id (String) - Formally defines the assertion.

Assertion text (String) - Description output to the validation report.

Macro assertion id (List) - The assertion id identified in Gazelle assertion manager. Adding this linkage defines this micro assertion as supporting the indicated macro assertion.

Assertion group or assertion metadata

PTO - PTO stands for Profile/Transaction/Option, a context labeling scheme for assertions. Profiles, transactions, and options each have a ID labeling them. In this way PTO, in the form of a path or URN, is a context label for an assertion. As a path it would be written /profile/transaction/option. As a URN it would be

urn:Assertion:profileID:transactionID:optionID.  

A blank level such as urn:Assertion:xds:reg

would indicate that the assertion applies to any metadata in the XDS profile, Register transaction. It is not selective on option.

The PTO is specified as a list of PTO values with OR semantics. If the effective PTO (in the validator engine) is compatible with any PTO in this list then the assertion or assertion group is selected for execution.

Linkage between assertion groups and Actors/Transactions/Option

To know the validation requirements, the actor, transaction, and option(s) must be identified. Validator group(s) are optionally tagged with the profile, transaction, and option(s) (PTO metadata) that describes the grouped validators. A validator group with no PTO metadata inherits the PTO metadata from its parent. In defining this inheritance, a sub-group may add or remove PTO metadata tags thus declaring its context as a delta off its parent PTO. This delta could be specified in two ways: as a new absolute PTO that applies to this group and its children, overriding the parent PTO, or as a delta off the parent PTO. No syntax is established to describe PTO deltas (yet).

Reuse of assertion groups - inheritance

Assertion groups are reusable. An example is the Register On-Demand Document transaction. It is a Register transaction with some assertions added and some removed. This transaction should be defined separately from the Register transaction, should inherit the assertions defined for Register, and define a delta off its parent. The delta would remove some validation requirements and add new ones.

Note that the following examples exist to identify semantics. This is not a syntax proposal.

A metadata element such as

inherits urn:Assertion:xds:register

in the top level On-Demand assertion group indicates that all assertions selected by urn:Assertion:xds:register apply in this group. Assertions defined in this group would add to that inherited set. Assertions from the above inherits statement to be excluded are

except AREG0034, AREG0089, AREG2390

where these identifiers are assertion IDs defined for the Register transaction.

Note that the Java class system gives us referential integrity. These assertions are classes so the IDs for the assertions are really class references. If the classes do not exist then the build system will complain. In Java the reference would be AREG00334.class and in Groovy it would be just AREG0034. In both cases the package names are not shown for readability. In the code the package names would have to be present. Tool based refactoring would automatically update the package names and the assertion ids if necessary.

Order of execution of validator groups

The selection of validator groups to execute and the order of their execution is controlled by a validator classpath.

Assertion class

Assertion class are identified through Java Reflection based on an interface. This interface includes

public interface Assertion {
    List<String> ptos;
    Class<?> getAssertionId();
    List<String> getMacroAssertions();
    String getText();
    boolean isOptional();
    boolean execute();
}

The isOptional value controls how the assertion is reported. Non-optional assertions that return false from execute() fail. Optional assertions that return false from execute() report the status as an informational message instead of as a failure.

Assertion group class

Assertion group classes are optional but if present there may be a single class of this type in a package. A sub-package may have its own assertion group class. The assertion group class is identified by its implementing an interface

public interface AssertionGroup {
    List<String> getMacroAssertions();
    List<Class> getInherits();
    List<Class> getExcepts();
    boolean execute();
}

Note that getInherits() must return classes, not packages, but it is intended to reference assertion groups, not assertions. So getInherits() must reference assertion group classes. This implies that even though having an assertion group class in an assertion group is optional, it is required if another assertion group references it through an inherits relationship.

Macro assertions are maintained in Gazelle with string-based identifiers.

If an assertion group class is missing (it is optional) then the validation system has a default implementation. In most cases the only reason to define an assertion group is to specifiy the metadata. There will be a base implementation of this class, AssertionGroupBase, that will implement the default execute method. Only in special cases will this need to be overridden.

Types of assertions

There are a collection of assertion types, each defined by an (probably) abstract base class that is extended. Known assertion types are

IsNull

IsNotNull

IsEquals

IsLessThan ...

IsContainedIn

IsNotContainedIn

IsSubStringOf

IsNotSubStringOf

Examples

Slot validation

There are several parts to validating a slot

Single or multi-value - boolean passed to Slot#validate

Allowed/required in this object - handled in the parent object

Format of the values - this is delegated to a FormatValidator which is passed into th Slot#validate method.

In part these are built into the current AbstractRegistryObject class which DocumentEntry extends:

abstract public void validateSlotsLegal(ErrorRecorder er);
abstract public void validateRequiredSlotsPresent(ErrorRecorder er, ValidationContext vc);
abstract public void validateSlotsCodedCorrectly(ErrorRecorder er, ValidationContext vc);

The parent object all extend AbstractRegistryObject which has the validationSlot method. A call is made for each slot that is allowed. The calls are made from DocumentEntry.validateSlotsCodedCorrectly. A separate validation is made to determine if the slot is acceptable. This is driven off of List DocumentEntry.definedSlots and List<String DocumentEntry.requiredSlots.

These types of slot validators are needed: slot name acceptable, slot name required, multivalued, value format.

List<String> pto = ['urn:Assertion:xds:pnr', 'urn:Assertion:xds:r']
String assertionId = 'xxxx'
String macroAssertion = 'yyyy'
String text = 'DocumentEntry acceptable Slot name'
List<String> slotNames = [
    'creationTime',
    'languageCode'    // ...
]
public boolean execute() { slotNames.contains slot.name }

Where does slot.name come from? What makes this DocumentEntry specific? These need to come from some context object. Who generates this context object?

AbstractRegistryObject has List. This assertion could accept an interface as a parameter containing

List<Slot> getSlots()

What links this to a DocumentEntry? There could be a factory/manager call

getAssertions(SlotAssertion, DocumentEntryContext).each { it.execute() }

BUT THIS DOES NOT ITERATE ACROSS ALL SLOTS IN THE DOCUMENTENTRY!!!!!!! This has to be nested inside something that does.

where getAssertions returns

List<Assertion>

The above assertion class could be

implements SlotAssertion, DocumentEntryContext

which could be just marker interfaces used by Reflection API.

The getAssertions method takes care of the finding of the assertion classes and building the instances. The finding would be to match the two marker interfaces (SlotAssertion, DocumentEntryContext) with the class definition of the assertion which would carry the above implements clause.

The assertion class has grown to

class MyAssertion implements SlotAssertion, DocumentEntryContext {
    List<String> pto = ['urn:Assertion:xds:pnr', 'urn:Assertion:xds:r']
    String assertionId = 'xxxx'
    String macroAssertion = 'yyyy'
    String text = 'DocumentEntry acceptable Slot name'
    List<String> slotNames = [
        'creationTime',
        'languageCode'    // ...
    ]
    
    public boolean execute() { slotNames.contains slot.name }
}

The marker interfaces SlotAssertion and DocumentEntryContext make the validator findable. The pto value declares the PTO context. AssertionId and macroAssertion support tooling. Text supports reporting. SlotNames supports the implementation of the execute method.

To make the code usable, getAssertions would have to be more like

getAssertions(List<Class<?>>)

Is that doable? Yes.

That handles the callee. What about the caller?

There would have to be a DocumentEntry assertion group tied to pnr and r assertions (matching this assertion). The execute method would use a bunch of yet undefined helper classes to iterate through the guts of a DocumentEntry.

Open Issues

Identify the validation models used:

  • Metadata class

  • Assertion class

Validation structure

MessageValidatorFactory#validateBasedOnValidationContext becomes ValidationContextValidationFactory

vs

MessageValidatorFactory#validateBasedOnRootElement becomes RootElementValidationFactory

separate out into two different factory classes

Clone this wiki locally