[Bean Validation] hasConstraints changes and a few polishing
by Emmanuel Bernard
I moved
hasConstraints() to the ElementDescritpor
which means that BeanDescriptor, PropertyDescritpor do inherit from
it. Make sense.
I also added a isBeanConstrained() to BeanDescriptor to know if a
given class will be impacted by validation (namely if any of it's
field, method or class / superclass / interfaces is annotated with a
constraint or with @Valid). It is a flag to take care of or ignore a
given class.
I also renamed BeanDescriptor.getPropertiesWithConstraints to
BeanDescriptor.getConstrainedProperties.
I am not too happy with the namings though, especially
isBeanConstrained(), so any improvement proposal is welcome.
5.2. ElementDescriptor
ElementDescriptor is the root interface describing elements hosting
constraints. It is used to describe the list of constraints for a
given element (whether it be a field, a method or a class).
/**
* Describes a validated element (class, field or property).
*
* @author Emmanuel Bernard
* @author Hardy Ferentschik
*/
public interface ElementDescriptor {
/**
* return true if at least one constraint declaration is present on
the element.
*/
boolean hasConstraints();
/**
* @return Statically defined returned type.
*
* @todo should it be Type or even completly removed
*/
Class<?> getType();
/**
* @return All the constraint descriptors for this element.
*/
List<ConstraintDescriptor> getConstraintDescriptors();
}
getType returns either the object type for a class, or the returned
type for a property (TODO problem of generics resolution).
getConstraintDescriptors returns the ConstraintDescriptors (see
Section 5.5, “ConstraintDescriptor”), each
ConstraintDescriptordescribing one of the constraints declared on the
given element.
hasConstraints returns true if the element (class, field or property)
holds at lease one constraint declaration.
5.3. BeanDescriptor
The BeanDescriptor interface describes a constrained Java Bean. This
interface is returned by Validator.getConstraintsForClass(Class<?>).
/**
* Describe a constrained Java Bean and the constraints associated to
it.
*
* @author Emmanuel Bernard
*/
public interface BeanDescriptor extends ElementDescriptor {
/**
* Returns true if the bean involves validation:
* - a constraint is hosted on the bean itself
* - a constraint is hosted on one of the bean properties
* - or a bean property is marked for cascade (@Valid)
*
* @return true if the bean nvolves validation
*
*/
boolean isBeanConstrained();
/**
* Return the property level constraints for a given propertyName
* or null if either the property does not exist or has no constraint
* The returned object (and associated objects including
ConstraintDescriptors)
* are immutable.
*
* @param propertyName property evaludated
*/
PropertyDescriptor getConstraintsForProperty(String propertyName);
/**
* return the property names having at least a constraint defined or
marked
* as cascaded (@Valid)
*/
Set<String> getConstrainedProperties();
}
isBeanConstrained returns true if the given class (and superclasses
and interfaces) host at least one validation declaration (either
constraint or@Valid annotation). If the method returns false, the Bean
Validation engine can safely ignore the bean as it will not be
impacted by validation.
getConstraintsForProperty returns a PropertyDescriptor object
describing the property level constraints (See Section 3.1.2, “Field
and property validation”). The property is uniquely identified by its
name as per the JavaBeans convention: field level and getter level
constraints of the given name are all returned.
getConstrainedProperties returns the names of the bean properties
having at least one constraint or being cascaded (@Valid annotation).
5.4. PropertyDescriptor
The PropertyDescriptor interface describes a constrained property of a
Java Bean. This interface is returned
byBeanDescriptor.getConstraintsForProperty(String). Constraints
declared on the attribute and the getter of the same name according to
the Java Bean rules are returned by this descriptor.
/**
* Describes a Java Bean property hosting validation constraints.
*
* Constraints placed on the attribute and the getter for a given
property
* are all referenced by this object.
*
* @author Emmanuel Bernard
*/
public interface PropertyDescriptor extends ElementDescriptor {
/**
* Is the property marked by the <code>@Valid</code> annotation.
* @return true if the annotation is present
*/
boolean isCascaded();
/**
* Name of the property acording to the Java Bean specification.
* @return property name
*/
String getPropertyName();
}
The isCascaded method returns true if the property is marked with
@Valid.
getPropertyName returns the property name as described in Section 4.2,
“ConstraintViolation”.
15 years, 11 months
[UPDATED] implement criteria API query of collection-of-components and collection-of-values
by David Mansfield
Hi,
Although I've gotten feedback that this won't necessarily be included
as-is, or against current versions etc., since I have to update it as I
track newer Hibernate core versions (we use this modification
internally), I figure it might be of use or interest to the dev.
community.
This is the updated patch, against 3.3.1GA
Changes from last time include:
* rediffed against 3.3.1
* change to slf4j for logging
* fix the crude hack in CriteriaJoinWalker to what seems to be the
correct implementation. The issue here is complicated and not-at-all
understood by me completely.
The method in question, generateTableAlias, seems to do (at least) two
things in the CriteriaJoinWalker, one of which is to update the internal
'userAlias' list based on whether the Joinable 'consumesEntityAlias'.
This behavior needed to remain completely unmodified, which my previous
patch did not do. However, the reason I needed to modify this is that
some Joinables which DO NOT 'conumeEntityAlias' still need to provide an
SQL alias for the table.
Deciding which ones, exactly, need to do this was complicated, and I'm
not sure this is currently correct.
However, it now passes the test suite, which it didn't before.
Thanks,
David
15 years, 11 months
Spec with all renamings
by Emmanuel Bernard
I have finished all the renamings we discussed before:
- MessageResolver => MessageInterpolator
- constraint related renamings
- bootstrap API renaming
I would appreciate additional pairs of eyes.
PS: I also made the changes to the RI.
15 years, 11 months
Spec with all renamings
by Emmanuel Bernard
I have finished all the renamings we discussed before:
- MessageResolver => MessageInterpolator
- constraint related renamings
- bootstrap API renaming
I would appreciate additional pairs of eyes.
PS: I also made the changes to the RI.
15 years, 11 months
[Bean Validation] MessageInterpolator improved algorithm
by Emmanuel Bernard
In the previous algorithm, parameter values were themselves
interpolated which was undesirable and could lead to security issue.
I have reworked the algorithm to resolve the custom ResourceBundle in
priority, the built-in bundle as a back up and finally regular
parameters.
Check it out
4.3. Message interpolation
4.3.1. Default message interpolation
A conforming implementation includes a default message interpolator.
This message interpolator shall use the algorithm defined here to
interpolate message descriptors into human-readable messages.
Each constraint defines a message descriptor via its message property.
Every constraint definition shall define a default message descriptor
for that constraint. Messages can be overridden at declaration time in
constraints by setting the message property on the constraint.
The message descriptor is a string literal and may contain one or more
message parameters. Message parameters are string literals enclosed in
braces.
Example 4.1. Message using parameters
Value must be between {min} and {max}
4.3.1.1. Algorithm
The default message interpolator uses the following steps:
Message parameters are extracted from the message string and used as
keys to search the ResourceBundle named ValidationMessages (often
materialized as the property file/ValidationMessages.properties and
its locale variations) using the defined locale (see below). If a
property is found, the message parameter is replaced with the property
value in the message string. Step 1 is applied recursively until no
replacement is performed (ie. a message parameter value can itself
contain a message parameter).
Message parameters are extracted from the message string and used as
keys to search the Bean Validation provider's built-in ResourceBundle
using the defined locale (see below). If a property is found, the
message parameter is replaced with the property value in the message
string. Contrary to step 1, step 2 is not processed recursively.
If step 2 triggers a replacement, then step 1 is applied again.
Otherwise step 4 is performed.
Message parameters are extracted from the message string. Those
matching the name of an attribute of the constraint declaration are
replaced by the value of that attribute.
The defined locale is as followed:
if the locale is passed to the interpolator method interpolate(String,
CosntraintDescriptor, Object, Locale), this Locale instance is used.
otherwise, the default Locale as provided by Locale.getDefault() is
used.
The proposed algorithm ensures that custom resource bundle always have
priority over built-in resource bundle at all level of the recursive
resolution. It also ensures that constraint declarations attributes
values are not expanded further.
4.3.2. Custom message interpolation
A custom message interpolator may be provided (e.g., to interpolate
contextual data, or to adjust the default Locale used). A message
interpolator implements the MessageInterpolator interface.
/**
* Interpolate a given constraint violation message.
*
* @author Emmanuel Bernard
* @author Hardy Ferentschik
*/
public interface MessageInterpolator {
/**
* Interpolate the message from the constraint parameters and the
actual validated object.
* The locale is defaulted according to the
<code>MessageInterpolator</code> implementation
* See the implementation documentation for more detail.
*
* @param message The message to interpolate.
* @param constraintDescriptor The constraint descriptor.
* @param value The object being validated
*
* @return Interpolated error message.
*/
String interpolate(String message,
ConstraintDescriptor constraintDescriptor,
Object value);
/**
* Interpolate the message from the constraint parameters and the
actual validated object.
* The Locale used is provided as a parameter
*
* @param message The message to interpolate.
* @param constraintDescriptor The constraint descriptor.
* @param value The object being validated
* @param locale the locale targeted for the message
*
* @return Interpolated error message.
*/
String interpolate(String message,
ConstraintDescriptor constraintDescriptor,
Object value,
Locale locale);
}
message is the message descriptor as seen in
@ConstraintAnnotation.message or provided to the ConstraintContext
methods.
constraintDescriptor is the ConstraintDescriptor object representing
the metadata of the failing constraint (see Constraint metadata
request API).
value is the value being validated.
MessageInterpolator.interpolate(String, ConstraintDescriptor, Object)
is invoked by for each constraint violation report generated. The
default Locale is implementation specific.
MessageInterpolator.interpolate(String, ConstraintDescriptor, Object,
Locale) can be invoked by a wrapping MessageInterpolator to enforce a
specific Locale value by bypassing or overriding the default Locale
strategy.
A message interpolator implementation shall be threadsafe.
The message interpolator is provided to the ValidatorFactory at
construction time using
ValidatorFactoryBuilder.messageInterpolator(MessageInterpolator). This
message interpolator is shared by all validators generated by this
ValidatorFactory.
It is possible to override the MessageInterpolator implementation for
a given Validator instance by
invokingValidatorFactory
.defineValidatorState
().messageInterpolator(messageInterpolator).getValidator().
It is recommended that MessageInterpolator implementations delegate
final interpolation to the Bean Validation default MessageInterpolator
to ensure standard Bean Validation interpolation rules are followed,
The default implementation is accessible through
ValidatorFactoryBuilder.getDefaultMessageInterpolator().
4.3.3. Examples
These examples describe message interpolation based on the default
message interpolator's built-in messages (see Appendix B, Standard
ResourceBundle messages), and the ValidationMessages.propertiesfile
shown in table Table 4.2, “message interpolation”. The current locale
is assumed English.
//ValidationMessages.properties
myapp.creditcard.error=credit card number not valid
Table 4.2. message interpolation
Failing constraint declaration interpolated message
@NotNull must not be null
@Max(30) must be less than or equal to 30
@Size(min=5, max=15, message="Key must have between {min} and {max}
characters") Key must have between 5 and 15 characters
@Digits(integer=9, fraction=2) numeric value out of bounds (<9
digits>.<2 digits> expected)
@CreditCard(message={myapp.creditcard.error}) credit card number not
valid
Here is an approach to specify the Locale value to choose on a given
Validator. Locale aware MessageInterpolator. See Section 4.4,
“Bootstrapping” for more details on the APIs.
Example 4.2. Use MessageInterpolator to use a specific Locale value
/**
* delegates to a MessageInterpolator implementation but enforce a
given Locale
*/
public class LocaleSpecificMessageInterpolator implements
MessageInterpolator {
private final MessageInterpolator defaultInterpolator;
private final Locale defaultLocale;
public LocaleSpecificMessageInterpolator(MessageInterpolator
interpolator, Locale locale) {
this.defaultLocale = locale;
this.defaultInterpolator = interpolator;
}
/**
* enforece the locale passed to the interpolator
*/
public String interpolate(String message,
ConstraintDescriptor
constraintDescriptor,
Object value) {
return defaultInterpolator.interpolate(message,
constraintDescriptor,
value, this.defaultLocale);
}
// no real use, implemented for completeness
public String interpolate(String message,
ConstraintDescriptor
constraintDescriptor,
Object value,
Locale locale) {
return defaultInterpolator.interpolate(message,
constraintDescriptor, value, locale);
}
}
Locale locale = getMyCurrentLocale();
MessageInterpolator interpolator = new
LocaleSpecificMessageInterpolator(
validatorFactory.getMessageInterpolator(),
locale);
Validator validator = validatorFactory.defineValidatorState()
.messageInterpolator
(interpolator)
.getValidator();
Most of the time, however, the relevant Locale will be provided by
your application framework transparently. This framework will
implement its own version of MessageInterpolator and pass it during
theValidatorFactory configuration. The application will not have to
set the Locale itself. This example shows how a container framework
would implement MessageInterpolator to provide a user specific default
locale.
Example 4.3. Contextual container possible MessageInterpolator
implementation
public class ContextualMessageInterpolator {
private final MessageInterpolator delegate;
public ContextualMessageInterpolator(MessageInterpolator
delegate) {
this.delegate = delegate;
}
public String interpolate(String message, ConstraintDescriptor
constraintDescriptor,
Object value) {
Locale locale = Container.getManager().getUserLocale();
return this.delegate.interpolate(
message, constraintDescriptor, value, locale );
}
public String interpolate(String message, ConstraintDescriptor
constraintDescriptor,
Object value, Locale locale) {
return this.delegate.interpolate(message,
constraintDescriptor, value, locale);
}
}
//Build the ValidatorFactory
ValidatorFactoryBuilder builder = Validation.getBuilder();
ValidatorFactory factory = builder
.messageInterpolator( new
ContextualMessageInterpolator
( builder.getDefaultMessageInterpolator() ) )
.build();
//The container uses the factory to validate constraints using the
specific MessageInterpolator
Validator validator = factory.getValidator();
15 years, 11 months
Branch 3.3 build fails
by Steve Kuo
I'm trying to build branch 3.3 using Maven and Java 1.5. I've followed the Maven build instructions (http://www.hibernate.org/422.html) exactly. When I build it fails on:
Results :
Tests in error:
testSessionFactoryCreationTime(org.hibernate.test.cfg.ConfigurationPerformanceTest)
Tests run: 1402, Failures: 0, Errors: 1, Skipped: 0
[INFO] ------------------------------------------------------------------------
[ERROR] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] There are test failures.
Am I missing something?
Thanks, Steve
15 years, 11 months
[BeanValidation] Naming adjustments for the Bootstrap API
by Emmanuel Bernard
The naming in the bootstrap API was lacking some consistency and was a
bit obscure at time
I went back tot he drawing board and came with something that is I
think more natural and thus easier to use:
- to build ValidatorFactory
- to build Validator
Each set of proposal change is followed by the old and new syntax.
Feedback welcome.
To Build a ValidatorFactory
Here are the proposed changes
ValidatorFactoryBuilder => Configuration (should it be
ValidatorFactoryConfiguration)
Validation.builderType(Class<T>) to Validation.byProvider(Class<T>)
Validation.defineBootstrapState() to Validation.byDefaultProvider()
GenericBuilderFactory.getBuilder() to
GenericConfigurationFactory.configure()
SpecializedBuilderFactory.getBuilder() to
SpecializedConfigurationFactory.configure()
ValidatorFactoryBuilder.build() to
Configuration.getValidatorFactory() (should it be
buildValidatorFactory()?)
ValidatorFactoryBuilder.configure() to
Configuration.customConfiguration()
Also I am considering:
- removing Validation.getBuilder() (which would have been
Validation.configure() )
- adding Validation.getDefaultValidatorFactory() (which correspond
to the default bootstrap strategy used by JPA and JSF unless overridden)
Here are various bootstraps with the old naming followed by the new
naming. You will see that we gain in consistency and expressivity:
- byProvider(Class<?>) vs byDefaultProvider()
- the object type retrieved and its purpose is more obvious
(configure(), getValidatorFactory())
- the builder is a configuration object retrieving state to build
ValidatorFactory
//simple bootstrap
ValidatorFactoryBuilder<?> builder = Validation.getBuilder();
=> no equivalent but replaced by
Configuration<?> configuration =
Validation.byDefaultProvider().configure();
Validation.getBuilder().build();
=>
ValidatorFactory factory = Validation.getDefaultValidatorFactory();
//use generic provider with specific resolver strategy
ValidatorFactoryBuilder<?> factoryBuilder =
Validation.defineBootstrapState()
.providerResolver(...) //optional step
.getBuilder();
=>
Configuration<?> configuration =
Validation.byDefaultProvider()
.providerResolver(...) //optional step
.configure();
//use a specific provider with specific resolver strategy
ValidatorFactoryBuilder<?> factoryBuilder =
Validation.builderType(ACMEValidatorFactoryBuilder.class)
.providerResolver(...) //optional step
.getBuilder();
=>
Configuration<?> configuration =
Validation.byProvider(ACMEValidatorConfiguration.class)
.providerResolver(...) //optional step
.configure();
I am also wondering if we should rename the methods on the
Configuration object (former ValidatorFactoryBuilder):
- messageResolver => useMessageResolver
- providerResolver => useProviderResolver
- traversableResolver => useTraversableResolver
- customConfiguration => useCustomConfiguration
- constraintFactory => useConstraintFactory
This makes them a bit more "fluent" but also more verbose.
To Build a Validator
rename ValidatorFactory.defineValidatorState() to
ValidatorFactory.usingContext()
rename ValidatorBuilder to ValidatorContext
Here are various bootstraps with the old naming followed by the new
naming. We gain consistency with the VF creation as well
//simple validator creation
Validator validator = validatorFactory.getValidator();
=>
Validator validator = validatorFactory.getValidator(); //unchanged
//with overriding context
Validator validator =
validatorFactory.defineValidatorState()
.traversableResolver(...)
.messageResolver(...)
.getValidator();
=>
Validator validator =
validatorFactory.usingContext()
.traversableResolver(...)
.messageResolver(...)
.getValidator();
What do you think?
There are three layers of changes:
- having a consistent naming between VF and V creations and using
more meaningful name for the build method (ie getValidator /
getValidatorFactory
- having a consistent path and naming wether you use the default
provider or a specific one
- rename the builder classes with meaningful definitions
15 years, 11 months
About HSEARCH-310
by Sanne Grinovero
Hello,
I'm in need of some help about how to fix this memory leak at HSEARCH-310
in ScrollableResultsImpl of Search, there currently is:
* an internal cache of all loaded EntityInfo
* an internal cache of all loaded managed Entities
Both caches are never emptied during the scrolling; the implementation
is loading a batch-sized list each time an entity is requested which
was not loaded before and adds new objects to the caches.
We had said to use some Soft ReferenceMap to fix this, but actually we
should evict the loaded entities which where not requested from the
client code, or I'll still be leaking memory in Hibernate's
persistence context.
I expect the most frequent use case for a ScrollableResults should be
to iterate at will and periodically evict objects in the client code;
Still the client could skip some entities we loaded by batching.
At first I thought to keep track of which references were used and
evict the other ones, but this isn't a good solution as the client
could be loading some entities by other means (i.e. another query) and
won't be happy if I'm evicting some random entities: I don't have
enough information to know for sure what to evict and what is going to
be used later.
Also a bug, consequence of current implementation, is that if you get
an object, then you evict it, and then try scrollable.get() you don't
get an attached object but again the evicted instance; as a client of
the Hibernate library I expect all objects I ask for to be attached by
default.
My idea to fix this should be something like:
1) load the batch
2) evict them all
3) and then load one by one as requested, relying on the second level
cache (I suppose point 1 should have warmed it)
But this has other flaws.. like I should not evict (2) those objects
already existing in the persistence context.
Ideally I should avoid to preload objects already present in the
persistence context; I am considering to look in the
session.persistenceContext.entitiesByKey map to only load+evict by
batch those entities not already managed.
Is there some way to populate the second level cache skipping the
actual load in the persistence context?
Should I keep my own "soft map cache" in the scroll implementation
anyway, and somehow detach and reattach them as needed?
Am I missing some trivial better solution, or is my solution not going
to work at all?
thanks,
Sanne
15 years, 11 months