Share this page to your:
Mastodon

Last entry I explained how, when using Madura Objects, you generate your Java Beans from an XSD file. The resulting classes have code injected into them by the Madura Objects JAXB plugin so that they validate automatically when the setters are called.

Madura Objects injects other things as well, notably metadata. With simple Java objects you don’t have much in the way of metadata. You have the field name, type and current value and that’s about it. Now that Java has annotations you can attach more static information to the field. Madura Objects uses annotations to define the validation data as well as the label. It generates code like this:

@Label(labelName = "xxx")
@Description(name = "this is a description")
@Regex(pattern = "a*b")
@Length(minLength = "0", maxLength = "30")
public String getName() {
    if (m_validationSession!= null) {
        m_validationSession.clean(this);
    }
    return name;
}

The Regex and Length annotations here drive the validation engine in Madura Objects. You can see a little of the generated code in the getName() method. Other validation related annotations are Digits, Email, Range and BeanValidator. The last can specify some custom Java code you can inject into the validator engine.

But it is not just about validation. The Label annotation is, fairly obviously, something a UI might use to paint next to the rendered field. There are others which declare the field to be read only, inactive (suggesting it ought not to be displayed), required etc. Description can be used for documentation or help text.

Remember these are all specified in the XSD file that generated this Java. You don’t have to edit them into the classes. In fact you really must not edit these files because next time you generate they will be overwritten. So how did we specify the label and description in the XSD?

<element name="name">
    <xsd:annotation>
        <xsd:appinfo>
            <annox:annotate>
                <md:Label labelName="xxx"/>
                <md:Description name="this is a description"/>
            </annox:annotate>
        </xsd:appinfo>
    </xsd:annotation>
    <simpleType>
        <restriction base="string">
            <maxLength value="30"></maxLength>
            <pattern value="a*b"></pattern>
        </restriction>
    </simpleType>
</element>

I’ve used the annox tool to help out here. This is another JAXB plugin which grabs my reference in the XSD and turns it into an annotation. Annox comes from the same people who produced HyperJAXB3. In practice this is all looked after transparently during the generation of the Java objects.

Another important bit of metadata we need is a list of valid values. We can do this two ways. The first way is just the ‘normal’ way expected by JAXB by itself. This looks like this:

<element name="business" type="tns:IndustryType"/>
...
<xsd:simpleType name="IndustryType">
    <xsd:restriction base="xsd:string">
        <xsd:enumeration value="Ag"/>
        <xsd:enumeration value="fish"/>
        <xsd:enumeration value="finance"/>
    </xsd:restriction>
</xsd:simpleType>

In this example we defined a field called business and pointed it at a simple type definition with a list of values. Remember this is JAXB out-of-the-box. The resulting code is a Java class called IndustryType, which is an enum, and a field that looks like this:

@Enumerated(EnumType.STRING)
public IndustryType getBusiness() {
    if (m_validationSession!= null) {
        m_validationSession.clean(this);
    }
    return business;
}

This, obviously, only accepts values from the enum which we know must be valid. The validation engine will check them anyway. This all works perfectly well as long as the list of values is quite static. To change the values you need to regenerate the Java.

If you need something more dynamic you can do this:

<element name="customerType">
    <xsd:annotation>
        <xsd:appinfo>
            <annox:annotate>
                <md:ChoiceList name="customerType"/>
            </annox:annotate>
        </xsd:appinfo>
    </xsd:annotation>
    <simpleType>
        <restriction base="string">
            <maxLength value="30"></maxLength>
        </restriction>
    </simpleType>
</element>

The important bit to note is the ChoiceList entry. Here is the generated code:

@ChoiceList(name = "customerType")
@Length(minLength = "0", maxLength = "30")
public String getCustomerType() {
    if (m_validationSession!= null) {
        m_validationSession.clean(this);
    }
    return customerType;
}

Probably no surprises there. But the validation engine knows about the ChoiceList and uses that instead of an enum. I won’t go into the Spring wiring that I use to arrange this (there are full examples in the project) but you create an XML document containing the choice lists and their contents. The file can be regenerated any time, making the valid choices easily revisable.

We can access this metadata very easily because the Madura Objects plugin generates the code in the classes to support it. The plugin adds the nz.co.senanque.validationengine.ValidationObject interface to the generated objects and this has a getMetadata() method which returns an ObjectMetadata object. This is just a wrapper for a list of FieldMetadata objects, and they describe the current metadata for the fields.

Yes, you could do this yourself using reflection. But the getMetadata() object is simpler and it also supports dynamic metadata. But dynamic metadata is a topic for next time. 

Previous Post Next Post