PathImpl.createCopy() only performs a shallow copy

Description

hen calling Validator.validateValue() with a class containing an array field with a single Pattern validation on the entire array which creates violations, the index in the path of the validations is only correct for the last item in the array. The index for the violations on other elements is always set to the index of the last element.

I spent some time debugging this and found the PathImpl.createCopy(), which calls the copy constructor PathImpl(PathImpl path), is only doing a shallow copy of the values. The validation for each element in the array is using the same instance of the nodeList property in PathImpl instead of a different one for each validation. This happens because the copy constructor does not create a new list, but uses the existing one. The code that changes the currentLeafNode values is then update that list which ends up in each violation path.

I believe the fix to be on line 351 of PathImpl in the 6.0.23.Final code would be to replace that line with this one.:

nodeList = new ArrayList<path.nodeList>() // Need null check for path.nodeList?

Environment

JDK zulu8.68.0.21-ca-jdk8.0.362-win_x64 Hibernate validator versions: hibernate-validator-6.0.23.Final.jar, hibernate-validator-6.2.5.Final.jar

Attachments

2
  • 02 May 2023, 12:51 AM
  • 02 May 2023, 12:50 AM

Activity

Show:

Marko Bekhta May 8, 2023 at 8:47 AM

I didn’t really know what I should have added as a node name so I just guessed nothing.

Built-in value extractors section in the spec describes the names used for built-in extractors. While you are not required to pass a name … Hibernate Validator logic around nodes and paths was built with the idea in mind that those iterable (multivalued) containers would have something as a container element.

That’s why if you do not pass anything as a node name, it messes up not only the paths in the constraint violations but also provides unexpected results when you call isInIterable() / getIndex()

The string for that node comes out as expected: componentValidatorText[0], but I expected that isInIterable() would return true and that getIndex()would return 0. But, they do not.

as for this part … there’s a bit of explanation in the Javadocs. You can also take a look at the Listing 6.4 of ConstraintViolation section. In short - both isInIterable() / getIndex() are intended for a container element and not for the container itself. So if you have a path like componentValidatorText[0].<some container element name> then <some container element name> node will return isInIterable() == true / getIndex() == 0 but node componentValidatorText[0] will be just considered a container so it is not in iterable and as a result doesn’t have an index – isInIterable() == false / getIndex() == null

Jay Gorrell May 5, 2023 at 6:14 PM

Thank you for your help! Adding the nodeName fixes this issue. I didn’t really know what I should have added as a node name so I just guessed nothing.

I have a related question for you though. It has to do with the PropertyNode in the path returned by constraintViolation.getPropertyPath(). The string for that node comes out as expected: componentValidatorText[0], but I expected that isInIterable() would return true and that getIndex()would return 0. But, they do not. They return false and null instead. My question is why is that? If there is a RTFM somewhere that I need to look at, just let me know. TIA

Marko Bekhta May 4, 2023 at 11:32 AM

Hey thanks for reporting the issue and providing the reproducibles. The Path implementation deliberately is not creating deep copies for performance and resource usage reasons. While that’s so, there’s a problem you’ve reported. It comes from the fact that you are passing a null value as a nodeName into receiver.indexedValue(..). This results in a created path missing a container element node; you can see that by inspecting the nodes in the path or by calling a Node#getIndex() either on the node or nodeImpl in your test case.

If you'd like to make it work on with the version of the validator you are using right now – you’d have to pass a nonnull node name. Keep in mind that that will change the string representation of the path (that’s if you care about how it looks).

In the meantime, I’ll submit a patch that should address the problem.

Fixed

Details

Assignee

Reporter

Bug Testcase Reminder (view)

Bug reports should generally be accompanied by a test case!

Bug Testcase Reminder (edit)

Bug reports should generally be accompanied by a test case!

Feedback Requested

Participants

Jay Gorrell
Marko Bekhta

Components

Fix versions

Affects versions

Priority

Created May 2, 2023 at 12:55 AM
Updated August 1, 2024 at 8:38 AM
Resolved July 21, 2024 at 1:21 PM

Flag notifications