-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 
Author Message
 Post subject: Validation unable to load xml mapping files at runtime.
PostPosted: Tue Aug 16, 2011 7:59 am 
Newbie

Joined: Tue Apr 26, 2011 4:46 am
Posts: 4
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


Top
 Profile  
 
 Post subject: Re: Validation unable to load xml mapping files at runtime.
PostPosted: Tue Aug 16, 2011 1:54 pm 
Hibernate Team
Hibernate Team

Joined: Sat Jan 24, 2009 12:46 pm
Posts: 388
Hi Thor,

your mapping file looks good, I ran a short test with it which was sucessful. Are there any custom constraints involved? The NPE you got should only occur when the constraint at hand is not a built in one (meaning it's not defined in BV/HV).

I think the best is if you provide a minimal test case demonstrating the problem, otherwise it's really hard to say anything more specific IMO.

--Gunnar

_________________
Visit my blog at http://musingsofaprogrammingaddict.blogspot.com/


Top
 Profile  
 
 Post subject: Re: Validation unable to load xml mapping files at runtime.
PostPosted: Tue Aug 16, 2011 3:37 pm 
Newbie

Joined: Tue Apr 26, 2011 4:46 am
Posts: 4
I don't have the code here so I can't grab the miscellaneous bits around it, but the code fragment in the first <code> box is pretty much all there is to it - other than the bit of code providing it with the resource name to give to the Configuration.addMapping method. I know it's not passing the isBuiltinType test, but I don't get why - the tutorial examples use the very same validators I've listed in my xml file. I'm not sure if it's because I'm loading them at runtime or not, but it fails 100% of the time.


Top
 Profile  
 
 Post subject: Re: Validation unable to load xml mapping files at runtime.
PostPosted: Wed Aug 17, 2011 3:43 am 
Newbie

Joined: Tue Apr 26, 2011 4:46 am
Posts: 4
Ok. I'll set up a minimal test case as best I can here:

Code:

Code:
    private void test() throws IOException
    {

        final Configuration<?> validatorConfiguration = Validation.byDefaultProvider().configure();
        final InputStream mappingStream = getClassLoader().getResourceAsStream("cdm-validation.xml");
        validatorConfiguration.addMapping(mappingStream);

        final ValidatorFactory vf = validatorConfiguration.buildValidatorFactory(); // Exception line.
        mappingStream.close();

    }

validation.xml:

Code:
<validation-config xmlns="http://jboss.org/xml/ns/javax/validation/configuration"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://jboss.org/xml/ns/javax/validation/configuration">

    <default-provider>org.hibernate.validator.HibernateValidator</default-provider>
    <message-interpolator>org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator</message-interpolator>
    <traversable-resolver>org.hibernate.validator.engine.resolver.DefaultTraversableResolver</traversable-resolver>
    <constraint-validator-factory>org.hibernate.validator.engine.ConstraintValidatorFactoryImpl</constraint-validator-factory>

</validation-config>

cdm-validation.xml:

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>

Exception (NullPointer as before):

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>.test(RegistrarImpl.java:452)


That's all the relevant code, which is in an ear running on jboss 5.1.0, using hibernate-validator-4.1.0.Final and validation-api-1.0.0.GA (jsr 303).

The Person Class is auto-generated by JAXB.

Update: I've tried this locally and it succeeds, but for some reason still fails on the jboss server.


Top
 Profile  
 
 Post subject: Re: Validation unable to load xml mapping files at runtime.
PostPosted: Thu Aug 18, 2011 3:11 am 
Newbie

Joined: Tue Apr 26, 2011 4:46 am
Posts: 4
I've found the problem so I'll post it here in case someone else gets this problem.

Annoyingly someone else had coded the war receiving the request and forwarding it to my code, and since their code depended on the validation-api.jar they had bundled it in their war file. This causes the Thread to have a contextClassLoader from Tomcat (that has access to the JSR 303 Annotations), but Hibernate populates its list of built-in Annotations using the ClassLoader available to my ear file, then uses the contextClassLoader to load the Annotations described in the xml. Clearly you get two different instances of the Class objects and it fails to detect it's a built-in Annotation.

I still think it odd that this problem causes Hibernate to throw a NullPointerException however, that's not terribly helpful in finding the problem.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 5 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.