I'm finding myself very stuck trying to add validation mapping files at runtime. I can't really enumerate all the things I've tried here, but I'll try to sum it up as best I can.
Since someone is bound to ask why I need to add the mapping files at runtime, it's because the requirements for this project is that it allows plug-ins that will be deployed afterwards to provide its own validation rules. And since I can't have multiple validation.xml files I need their mapping files loaded as and when they register themselves with my framework.
To that end my framework sets up its own MessageInterpolator to handle multiple sources and will create a new ValidationFactory from a Configuration<?> object any time the configuration changes (when a plug-in register or unregister); (slightly simplified code)
Code:
final List<InputStream> mappingStreams = new ArrayList<InputStream>();
Configuration<?> validatorConfiguration = Validation.byDefaultProvider().configure();
validatorConfiguration.messageInterpolator(messageInterpolator);
for (String mappingFile : validatorMappingFiles)
{
final InputStream mappingStream = getClassLoader().getResourceAsStream(mappingFile);
mappingStreams.add(mappingStream);
validatorConfiguration.addMapping(mappingStream);
}
try
{
return validatorConfiguration.buildValidatorFactory(); // Exception line.
}
finally
{
for (InputStream mappingStream : mappingStreams)
{
mappingStream.close();
}
}
I'm executing this bit of code as a test with a single xml mapping file provided:
Code:
<constraint-mappings xmlns="http://jboss.org/xml/ns/javax/validation/mapping"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/mapping validation-mapping-1.0.xsd">
<default-package>net.ubiquity.cdm.jaxb.model</default-package>
<bean class="Person" ignore-annotations="true">
<field name="userName">
<constraint annotation="javax.validation.constraints.NotNull">
<message>USERNAME_REQUIRED</message>
</constraint>
<constraint annotation="javax.validation.constraints.Min">
<message>USERNAME_TOO_SHORT</message>
<element name="value">2</element>
</constraint>
<constraint annotation="javax.validation.constraints.Max">
<message>USERNAME_TOO_LONG</message>
<element name="value">100</element>
</constraint>
</field>
</bean>
</constraint-mappings>
This gets the exception:
Code:
java.lang.NullPointerException
at org.hibernate.validator.metadata.ConstraintDescriptorImpl.findConstraintValidatorClasses(ConstraintDescriptorImpl.java:214)
at org.hibernate.validator.metadata.ConstraintDescriptorImpl.<init>(ConstraintDescriptorImpl.java:155)
at org.hibernate.validator.metadata.ConstraintDescriptorImpl.<init>(ConstraintDescriptorImpl.java:160)
at org.hibernate.validator.xml.XmlMappingParser.createMetaConstraint(XmlMappingParser.java:373)
at org.hibernate.validator.xml.XmlMappingParser.parseFieldLevelOverrides(XmlMappingParser.java:233)
at org.hibernate.validator.xml.XmlMappingParser.parse(XmlMappingParser.java:100)
at org.hibernate.validator.engine.ValidatorFactoryImpl.initXmlConfiguration(ValidatorFactoryImpl.java:174)
at org.hibernate.validator.engine.ValidatorFactoryImpl.<init>(ValidatorFactoryImpl.java:81)
at org.hibernate.validator.HibernateValidator.buildValidatorFactory(HibernateValidator.java:47)
at org.hibernate.validator.engine.ConfigurationImpl.buildValidatorFactory(ConfigurationImpl.java:154)
at <RegistrarImpl>.doGetValidatorFactory(RegistrarImpl.java:467)
I've stepped through the code at org.hibernate.validator.metadata.ConstraintDescriptorImpl.findConstraintValidatorClasses(ConstraintDescriptorImpl.java:214) using a remote debugger and found out that the annotation loaded by hibernate based on my xml mapping file does not match their built in types (even though as far as I can tell they should). Having noticed that I tried adding the following to the mapping files:
Code:
<constraint-definition annotation="javax.validation.constraints.NotNull">
<validated-by>
<value>org.hibernate.validator.constraints.impl.NotNullValidator</value>
</validated-by>
</constraint-definition>
<constraint-definition annotation="javax.validation.constraints.Min">
<validated-by>
<value>org.hibernate.validator.constraints.impl.MinValidatorForNumber</value>
<value>org.hibernate.validator.constraints.impl.MinValidatorForString</value>
</validated-by>
</constraint-definition>
<constraint-definition annotation="javax.validation.constraints.Max">
<validated-by>
<value>org.hibernate.validator.constraints.impl.MaxValidatorForNumber</value>
<value>org.hibernate.validator.constraints.impl.MaxValidatorForString</value>
</validated-by>
</constraint-definition>
This also does not work, instead I get:
Code:
Caused by: java.lang.ClassCastException: $Proxy2081 cannot be cast to javax.validation.constraints.NotNull
at org.hibernate.validator.constraints.impl.NotNullValidator.initialize(NotNullValidator.java:29)
at org.hibernate.validator.engine.ConstraintTree.initializeConstraint(ConstraintTree.java:302)
... 48 more
I'm using hibernate-validator-4.1.0.Final.
I'm sure I'm doing something wrong, but for some reason I can't seem to see it, so if anyone can help at all I'd be very grateful. Thanks and apologies for the long post.
Thor