Ok - so I tried to actually implement a JSR303 solution using the current API's and it while it is possible it has various limitations and is extremely ugly. If someone wants to post a better solution I would like to hear it. Since this must be a relatively common problem I want to share my solution for posterity.
Looking back at my original post, while something could be worked out annotation wise, the lack of a programatic API means for defining constraints (other than through XML) is a big hindrance to the 'bag-o-attributes' validation problem. In addition my post did not define a way to bind the constraint types to any sort of built-in attributes; so this whole idea would need a bit of work to flesh out properly.
Solution:Looking at what is going on under the hood ; it is also clear that the Hibernate implementation is deeply bound to the idea of a concrete java bean so I quickly dismissed the idea of breaking the code from its binding to concrete classes (give my desire to solve this in one day :).
Instead, I looked into by breaking my dynamic bag-of-attributes class into two parts:
(1) Predefined attributes
(2) Dynamic (user-defined) attributes
Code:
public final class Attribute {
...
}
Predefined Attribute Bag ProblemSolving the first problem was fairly straight forward so long as you do not mind programatically validating each field yourself.
First off, you can define a 'Base' definitions class that contains the base attributes you wish to have along with their corresponding constraints:
Code:
public static final class Base {
private Base() {}
@NotNull
@Pattern(flags=Flag.CASE_INSENSITIVE,regexp="\\w{6,6}")
@SuppressWarnings("unused") private String key;
@NotNull
@Pattern(flags=Flag.CASE_INSENSITIVE,regexp="\\w+ \\w+")
@SuppressWarnings("unused") private String displayName;
}
Given the above you can then programatically validate your attributes doing something like this:
Code:
if (attribute.isBaseAttr())
Set<ConstraintViolation<Base>> violations = validator.validateValue(Base.class, Names.DISPLAY_NAME, value);
Dynamic (user-defined) attributes problemThe second half of this, dynamic attributes are a bit more cumbersome, but work in the same way as the base attributes with the one caveat that you can only have a finite number of them.
First create an extensions class attribute holder given some large number of numbered fields:
Code:
public static class Ext {
private static final int maxIndex = n;
private int cindex = 0;
private Map<String,Integer> indexMap = ..;
public String getExtAttrInternalId(Attribute extAttr) {
Integer i = indexMap.get(extAttr.getName());
return "attribute" + i;
}
// attributes to bind values too
@SuppressWarnings("unused") private Object attribute0;
@SuppressWarnings("unused") private Object attribute1;
...
@SuppressWarnings("unused") private Object attributeN;
}
Then to configure the above you would use the standard JSR303 file with a definition for your 'Ext' attributes <bean class="Attribute$Ext">..</bean>. However before passing this XML file to the configuration mapping, need to run through the configuration file:
Code:
<bean class="Attribute$Ext">
<field name="foo"> ... </field>
<field name="bar"> ... </field>
</bean>
And convert your extension attribute names into their corresponding 'indexed' attribute names:
Code:
<bean class="Attribute$Ext">
<field name="attribute1"> ... </field>
<field name="attribute2"> ... </field>
</bean>
Finally the extension attributes can be programatically validate similar to the base attributes above:
Code:
else if (attribute.isExtAttr())
Set<ConstraintViolation<Base>> violations = validator.validateValue(Ext.class, Ext.instance().getExtAttrInternalId(attribute), value);
Minor code cleanliness improvements (as well as any further enhancements possibly needed for typing) can then be made by embedding the attribute holder class into the Attribute definition class, resulting in something like this:
Code:
Set<ConstraintViolation<Base>> violations = validator.validateValue(
attribute.getValidationCls(), attribute.getValidationId(), value);