[BV] New Path API
by Hardy Ferentschik
Hi,
I was integrating the new Path API into Hibernate Validator and here are
some thoughts about it.
#1 Would it be worth to have a getLeafNode() method in the API. In many
cases you need that the leaf
and the only way to get to it at the moment is to use the Iterator. It
would also help for my second point
#2 TraversableResolver.isReachable and TraversableResolver.isCascadable
both take in their current version a
Node (the leaf) and a Path (the parent path) as parameter. Why not just
pass a Path instance. Together
with getLeafNode() we would almost have everything we need ;-)
#3 The Path API only specifies methods to iterate existing Path instances.
But at some stage one has to build
these path instances as well. Since the API does not have a
Path.addNode() (or similar) I am forced to use
the Hibernate Validator specific PathImpl all over the place. Wouldn't
it make sense to also add addNode()
to the interface?
Even though ErrorBuilder pattern in ConstraintValidatorContext is quite
nice and the API allows now for proper
validating of Maps, there are still some semantic gaps in the overall API.
For example, Validator.validateValue
and Validator.validateProperty both take a String repesentation of the
property to be validated. If the validation
fails the returned ConstraintValidationException contains however a Path
instance describing the property path.
This makes it quite hard to compare the input property with the property
in the ConstraintViolation.
I am not 100% convinced by this new Path API yet. As an internal data
structure yes, but if it should be part of the
official API - not sure.
--Hardy
15 years, 6 months
Re: Pushing indexes through JGroups
by Emmanuel Bernard
Hello
I am not sure this is where we should go, or at least, it depends.
here are three scenarii
#1 JMS replacement
If you want to use JGroups as a replacement for the JMS backend, then
I think you should write a jgroups backend. Check
org.hibernate.search.backend.impl.jms
In this case all changes are sent via JGroups to a "master". The
master could be voted by the cluster possibly dynamically but that's
not necessary for the first version.
#2 apply indexing on all nodes
JGroups could send the work queue to all nodes and each node could
apply the change.
for various reasons I am not fan of this solution as it creates
overhead in CPU / memory usage and does nto scale very well from a
theoretical PoV.
#3 Index copy
this is what you are describing, copying the index using JGroups
instead of my file system approach. This might have merits esp as we
could diminish network traffic using multicast but it also require to
rethink the master / slave modus operandi.
Today the master copy on a regular basis a clean index to a shared
directory
On a regular basis, the slave go and copy the clean index from the
shared directory.
In your approach, the master would send changes to the slaves and
slaves would have to apply them "right away" (on their passive version)
I think #1 is more interesting than #3, we probably should start with
that. #3 might be interesting too, thoughts?
Emmanuel
PS: refactoring is a fact of life, so feel free to do so. Just don't
break public contracts.
On May 21, 2009, at 22:14, Łukasz Moreń wrote:
> Hi,
>
> I have few questions that concern using JGroups to copy index files.
> I think to create sender(for master) and receiver(slave) directory
> providers.
> Sender class mainly based on existing FSMasterDirectoryProvider,
> first create local index copy and send later to slave nodes
> (or send without copying, but that may cause lower performance?).
> To avoid code redundancy it would be good to refactor a little
> FSMasterDirectoryProvider class, so then I can use copying
> functionality in new DirectoryProvider and add sending one; or
> rather I should work around it?
>
> I do not understand completely how does the multithreading access to
> index file work. Does FileChannel class assure that, when index is
> copied and new Lucene works are pushed?
>
>
> I hope you had great holidays:)
>
> Lukas
15 years, 6 months
clone a lazy-init Object
by Tobias Meyer
Hello List,
we ran into a problem with javassist when recently upgrading to
hibernate 3.3.1.GA, whid did not occur with the previous cglib
implementation.
We sometimes make use of the cloneable interface and therefore have
declared a public clone() on some objects.
If those objects are initially loaded lazily, even if they are fully
initialized later on, a call to the clone() method will result in a
java.lang.IllegalAccessError at runtime.
This is caused by the Javassist proxy, which exposes the protected
clone() of java.lang.Object instead of the public definition from the
interface, which I believe to be a bug.
I submitted a proposed fix for this at
https://jira.jboss.org/jira/browse/JASSIST-85
<https://jira.jboss.org/jira/browse/JASSIST-85>
The result of the fix is, that the clone method of the target is called,
which returns a cloned instance of the target object (not a clone of the
proxy object), which is exactly what we need.
Maybe someone can give their 2 cent if this is actually desired behavior
or if this could cause unwanted side-effects.
I believe it's in every case better than the current state (runtime
errors), and others seem to have had similar issues as well
(see HHH-1726 and http://preview.tinyurl.com/mpnz9p)
Kind regards
Tobias Meyer
15 years, 6 months
[BV] Another border case
by Hardy Ferentschik
hi,
I have a question regarding another border case and the different validate
methods in Validator.
The javadocs for the vararg groups parameter says:
* @param groups groups targeted for validation
* (default to {@link javax.validation.groups.Default})
What if null is passed? I would think a IllegalArgumentException should be
raised. Or should the default
group be assumed?
--Hardy
15 years, 6 months
[Fwd: Re: Quick question about JTA-1.1.jar Dependency in Hibernate]
by Steve Ebersole
Grr
-------- Forwarded Message --------
From: Steve Ebersole <steve.ebersole(a)redhat.com>
To: hibernate-dev(a)lists.jboss.org <hibernate-dev(a)lists.jboss.org>
Cc: Shawn Stephens <shawndstephens(a)gmail.com>
Subject: Re: Quick question about JTA-1.1.jar Dependency in Hibernate
Date: Fri, 19 Jun 2009 12:08:19 -0500
Such requests should be sent to the dev list. I'll respond this time
directly (cc'ing the dev list).
We currently do not use TransactionSynchronizationRegistry. We may
later. But for now, it should be OK to use either version if you wish.
On Thu, 2009-06-18 at 19:46 -0500, Shawn Stephens wrote:
> One clarification, I meant "TransactionSynchronizationRegistry" below.
>
>
>
> On Jun 18, 2009, at 7:38 PM, Shawn Stephens wrote:
>
> > Mr. Ebersole,
> >
> >
> > Sorry to accost you via direct email, but I have a question. I
> > noticed in this commit you added JTA a required dependency in
> > Hibernate 3.3 Core's dist.xml file. The request was per this ticket.
> > Is it safe for me to use JTA-1.0.1B in lieu of JTA-1.1. Does
> > Hibernate depend on the TransactionSynchronizationManager added in
> > JTA 1.1? I have searched the entire core code base and found no
> > references. I'm assuming you just added the "default" version in the
> > JBoss maven repo, which is 1.1. Clarification on this would be
> > great.
> >
> >
> > Best Regards,
> > Shawn Stephens
>
>
--
Steve Ebersole <steve(a)hibernate.org>
Hibernate.org
15 years, 6 months
[BV] Limiting Default and @GroupSequence
by Emmanuel Bernard
Begin forwarded message:
> From: Emmanuel Bernard <emmanuel.bernard(a)jboss.com>
> Date: June 18, 2009 17:47:12 EDT
> To: Java Community Process JSR #303 Expert List <JSR-303-EG(a)JCP.ORG>
> Subject: Limiting Default and @GroupSequence
>
> In order to keep group sequences as orthogonal as possible to the
> idea of Default overriding I was thinking of restricting what can be
> done here.
> More specifically, I would like to add the following sentence in the
> spec:
>
> Groups part of a sequence overriding Default on a class Z
> (@GroupSequence) cannot be used in any other group or sequence.
>
> This would allow the sequence computations to be more easily
> independent of the class they are applied on. I was trying to find a
> use case where this would interfere but I have not so far.
>
> WDYT?
>
15 years, 6 months
[BV] Path, string or object model
by Emmanuel Bernard
The constraintValidatorContext fluent API for building errors. Please
review.
Begin forwarded message:
> From: Emmanuel Bernard <emmanuel.bernard(a)jboss.com>
> Date: June 18, 2009 16:55:06 EDT
> To: jsr-303-eg(a)jcp.org
> Subject: Re: [jsr-303-eg] Path, string or object model
>
> Here is the solution for ConstraintValidatorContext.
> I've designed a fluent API to create error reports and add their
> message and potentially their sub nodes and the appropriate
> contextual information.
>
> Here is a usage example
>
> public boolean isValid(String value, ConstraintValidatorContext
> context) {
> //default path
> context.buildErrorWithMessage( "this detail is wrong" ).addError();
>
> //default path + "street"
> context.buildErrorWithMessage( "this detail is wrong" )
> .inSubNode( "street" )
> .addError();
>
> //default path + "addresses["home"].country.name
> context.buildErrorWithMessage( "this detail is wrong" )
> .inSubNode( "addresses" )
> .inSubNode( "country" )
> .inIterable().atKey( "home" )
> .inSubNode( "name" )
> .addError();
> return false;
> }
>
> I've used a nested interface model to keep things clean. Please
> review and comment.
>
> /**
> * Provide contextual data and operation when applying a given
> constraint validator
> *
> * @author Emmanuel Bernard
> */
> public interface ConstraintValidatorContext {
> /**
> * Disable the default error message and default
> ConstraintViolation object generation.
> * Useful to set a different error message or generate a
> ConstraintViolation based on
> * a different property
> */
> void disableDefaultError();
>
> /**
> * @return the current uninterpolated default message
> */
> String getDefaultErrorMessage();
>
> /**
> * Return an error builder building an error allowing to optionally
> associate
> * the error to a sub path.
> * The error message will be interpolated.
> * <p/>
> * To create the error, one must call either one of
> * the #addError method available in one of the
> * interfaces of the fluent API.
> * If another method is called after #addError() on
> * ErrorBuilder or any of its associated nested interfaces
> * an IllegalStateException is raised.
> * <p/>
> * If <code>isValid<code> returns <code>false</code>, a
> <code>ConstraintViolation</code> object will be built
> * per error including the default one unless {@link
> #disableDefaultError()} has been called.
> * <p/>
> * <code>ConstraintViolation</code> objects generated from such a
> call
> * contain the same contextual information (root bean, path and so
> on) unless
> * the path has been overriden.
> * <p/>
> * To create a different error, a new error builder has to be
> retrieved from
> * ConstraintValidatorContext
> *
> * Here are a few usage examples:
> * <pre>//create new error with the default path the constraint
> * //is located on
> * context.buildErrorWithMessage( "way too long" )
> * .addError();
> *
> * //create new error in the "street" subnode of the default
> * //path the constraint is located on
> * context.buildErrorWithMessage( "way too long" )
> * .inSubNode( "street" )
> * .addError();
> *
> * //create new error in the "addresses["home"].city.name
> * //subnode of the default path the constraint is located on
> * context.buildErrorWithMessage( "this detail is wrong" )
> * .inSubNode( "addresses" )
> * .inSubNode( "country" )
> * .inIterable().atKey( "home" )
> * .inSubNode( "name" )
> * .addError();
> * </pre>
> *
> * @param message new uninterpolated error message.
> */
> ErrorBuilder buildErrorWithMessage(String message);
>
> /**
> * Error builder allowing to optionally associate
> * the error to a sub path.
> *
> * To create the error, one must call either one of
> * the #addError method available in one of the
> * interfaces of the fluent API.
> * If another method is called after #addError() on
> * ErrorBuilder or any of its associated objects
> * an IllegalStateException is raised.
> *
> */
> interface ErrorBuilder {
> /**
> * Add a subNode to the path the error will be associated to
> *
> * @param name property
> * @return a builder representing the first level node
> */
> NodeBuilder inSubNode(String name);
>
> /**
> * Add the new error report to be generated if the
> * constraint validator mark the value as invalid.
> * Methods of this ErrorBuilder instance and its nested
> * objects returns IllegalStateException from now on.
> *
> * @return ConstraintValidatorContext instance the ErrorBuilder
> comes from
> */
> ConstraintValidatorContext addError();
>
> /**
> * Represent asubnode whose context is known
> * (ie index, key and isInIterable)
> */
> interface NodeBuilder {
> /**
> * Add a subNode to the path the error will be associated to
> *
> * @param name property
> * @return a builder representing this node
> */
> InIterableNodeBuilder inSubNode(String name);
>
> /**
> * Add the new error report to be generated if the
> * constraint validator mark the value as invalid.
> * Methods of the ErrorBuilder instance this object comes
> * from and the error builder nested
> * objects returns IllegalStateException from now on.
> *
> * @return ConstraintValidatorContext instance the ErrorBuilder
> comes from
> */
> ConstraintValidatorContext addError();
> }
>
> /**
> * Represent a subnode whose context is
> * configurable (ie index, key and isInIterable)
> */
> interface InIterableNodeBuilder {
> /**
> * Mark the node as being in an Iterable or a Map
> * @return a builder representing iterable details
> */
> InIterablePropertiesBuilder inIterable();
>
> /**
> * Add a subNode to the path the error will be associated to
> *
> * @param name property
> * @return a builder representing this node
> */
> InIterableNodeBuilder inSubNode(String name);
>
> /**
> * Add the new error report to be generated if the
> * constraint validator mark the value as invalid.
> * Methods of the ErrorBuilder instance this object comes
> * from and the error builder nested
> * objects returns IllegalStateException from now on.
> *
> * @return ConstraintValidatorContext instance the ErrorBuilder
> comes from
> */
> ConstraintValidatorContext addError();
> }
>
> /**
> * Represent choices for a node which is
> * in an Iterator or Map.
> * If the iterator is an indexed collection or a map,
> * the index or the key should be set.
> */
> interface InIterablePropertiesBuilder {
> /**
> * Define the key the object is into the Map
> *
> * @param key map key
> * @return a builder representing the current node
> */
> NodeBuilder atKey(Object key);
>
> /**
> * Define the index the object is into the List or array
> *
> * @param index index
> * @return a builder representing the current node
> */
> NodeBuilder atIndex(Integer index);
>
> /**
> * Add a subNode to the path the error will be associated to
> *
> * @param name property
> * @return a builder representing this node
> */
> InIterableNodeBuilder inSubNode(String name);
>
> /**
> * Add the new error report to be generated if the
> * constraint validator mark the value as invalid.
> * Methods of the ErrorBuilder instance this object comes
> * from and the error builder nested
> * objects returns IllegalStateException from now on.
> *
> * @return ConstraintValidatorContext instance the ErrorBuilder
> comes from
> */
> ConstraintValidatorContext addError();
> }
> }
> }
>
>
> On Jun 17, 2009, at 14:41, Emmanuel Bernard wrote:
>
>> Here is the solution I cooked. I think it's acceptable but add some
>> complexity in the ConstraintValidatorContext API (see other email).
>> Please review (pay special attention to the examples).
>>
>> A Path represents the path and is the one accepted by the path
>> consuming APIs. A Path is an Iterable of Nodes.
>>
>> /**
>> * Represent a navigation path from an object to another.
>> * Each path element is represented by a Node.
>> *
>> * The path corresponds to the succession of nodes
>> * in the order they are retured by the Iterator
>> *
>> * @author Emmanuel Bernard
>> */
>> public interface Path extends Iterable<Node> {
>> }
>>
>>
>> A node represent a path element.
>>
>> /**
>> * Represents an element of a navigation path
>> *
>> * @author Emmanuel Bernard
>> */
>> public interface Node {
>> /**
>> * Property name the node represents
>> * or null if the leaf node and representing an entity
>> * (in particular the node representing the root object
>> * has its name null)
>> */
>> String getName();
>>
>> /**
>> * True if the node represents an object contained in an Iterable
>> * or in a Map.
>> */
>> boolean isInIterable();
>>
>> /**
>> * The index the node is placed in if contained
>> * in an array or List. Null otherwise.
>> */
>> Integer getIndex();
>>
>> /**
>> * The key the node is placed in if contained
>> * in a Map. Null otherwise.
>> */
>> Object getKey();
>> }
>>
>> A few interesting points:
>> - the index / key is hosted by the node after the collection node
>>
>> Here are a few examples and their Node equivalent
>>
>> ""
>> 0: Node(name:null, isInIterable:false, index:null, key:null)
>>
>> "email"
>> 0: Node(name:email, isInIterable:false, index:null, key:null)
>>
>> "addresses"
>> 0: Node(name:addresses, isInIterable:false, index:null, key:null)
>>
>> "addresses["home"]" represent the bean level of an Address object
>> 0: Node(name:addresses, isInIterable:false, index:null, key:null)
>> 1: Node(name:null, isInIterable:true, index:null, key:home)
>>
>> "addresses["home"].city"
>> 0: Node(name:addresses, isInIterable:false, index:null, key:null)
>> 1: Node(name:city, isInIterable:true, index:null, key:home)
>>
>> "billingAddresses[3].country.name"
>> 0: Node(name:billingAddresses, isInIterable:false, index:null,
>> key:null)
>> 1: Node(name:country, isInIterable:true, index:3, key:null)
>> 2: Node(name:name, isInIterable:false, index:null, key:null)
>>
>>
>> ConstraintViolation renders a Path
>>
>> public interface ConstraintViolation<T> {
>> Path getPropertyPath();
>> }
>>
>> TraversableResolver accepts a path
>>
>> public interface TraversableResolver {
>> boolean isReachable(Object traversableObject,
>> String traversableProperty,
>> Class<?> rootBeanType,
>> Path pathToTraversableObject,
>> ElementType elementType);
>> ...
>> }
>>
>> PS: should String traversableProperty be Node traversableProperty ?
>>
>>
>> On May 27, 2009, at 12:19, Emmanuel Bernard wrote:
>>
>>> In several areas we do describe path:
>>> - ConstraintViolation
>>> - ConstraintValidatorContext (with addError(String, String) which
>>> allows to concatenate substrings
>>>
>>> So far we use the notion of string to represent it
>>> - address.name
>>> - cards[3].color
>>> - addresses["home"].city
>>>
>>> I have added the idea of using [] for simple Iterable objects (ie
>>> non indexed, like a Set)
>>> - accounts[].balance
>>>
>>> Anybody objects to that?
>>>
>>> Second point
>>> Do we want to replace this String approach with a path object mode?
>>>
>>> http://opensource.atlassian.com/projects/hibernate/browse/BVAL-143
>>> ______
>>> path are today strings with dot separating properties. But it
>>> break when Set or Iterable are used.
>>> We could replace that with
>>> --- First strawman, must evolve --
>>> class PathElement {
>>> String getName();
>>> PathElement getParentPath();
>>> boolean isIterable();
>>> boolean isIndexed();
>>> Object getIndex();
>>> //TODO int getIndex()?
>>>
>>> // not happy about that as it is only useful for
>>> Constraintciolation
>>> PathElement getChild();
>>> }
>>>
>>> PathElement would be used for Constraintvuilation, maybe CVContext
>>> etc
>>>
>>> can this be refactored using inheritance + generics to have an
>>> IndexedPathElement only when it matters (probably no unfortunately)
>>> ______
>>>
>>>
>>> Pros:
>>> - less string manipulation by the user and the
>>> TraversableResolver implementation
>>> - the map index no longer rely on "[" + toString() + "]" and is
>>> likely more easily handled
>>>
>>> Cons:
>>> - ConstraintValidatorContext becomes more complex as it needs to
>>> expose some kind of path element builder.
>>> - we would like need to standardize some kind of String
>>> serialization anyway
>>> - I don't see Pros as huge advantages
>>>
>>> WDYT?
>>
>> _______________________________________________
>> jsr-303-eg mailing list jsr-303-eg(a)jcp.org
>
15 years, 6 months
Re: Groups are represented by interfaces
by Emmanuel Bernard
On Jun 18, 2009, at 09:36, Hardy Ferentschik wrote:
> Hi,
>
> another question. The spec says that groups are interfaces, but it
> is not specific about what happens if non interface classes
> are passed to validate:
>
> /**
> * Validates all constraints on object.
> *
> * @param object object to validate
> * @param groups groups targeted for validation
> * (default to {@link javax.validation.groups.Default})
> *
> * @return constraint violations or an empty Set if none
> *
> * @throws IllegalArgumentException if object is null
> * @throws ValidationException if a non recoverable error happens
> * during the validation process
> */
> <T> Set<ConstraintViolation<T>> validate(T object, Class<?>...
> groups);
>
> Should an IllegalArgumentException be thrown if the class is not an
> interface?
I don't want the spec to limit innovation, That's why the exception
model is not specified. Some product can support Class groups.
>
> Related to this:
> "Since sequences cannot have circular dependencies, using Default in
> the declaration of a sequence is not an op-
> tion. Constraints hosted on a class A and belonging to the Default
> group (by default or explicitly) implicitly belong
> to the group A."
>
> Does this imply I should be able to call
> Validator.validate(aInstance, A.class)? That would violate the
> statement that
> groups are interfaces. Is group A just a "conceptual" group?
Yes A is a conceptual group.
PS: let's have these discussions on hibernate-dev. People can share
that and that will be searchable.
>
> --Hardy
15 years, 6 months