From c9a9948f1e9fd0d7fa9b08b8073d0e25ab8a508e Mon Sep 17 00:00:00 2001 From: Dmitriy Tverdiakov Date: Mon, 21 Jul 2025 22:24:03 +0100 Subject: [PATCH] fix(object-mapping): try making record components accessible In some cases reading components of `record` may fail as their accessors may not be accessible. This update makes sure the driver tries to make them accessible. For instance, this is useful when using local `record` in a stricter runtime. The `ObjectMappingTests` actually had a test for this, but it did not fail when it was run as a unit test. The tests have been moved to integration tests without any other update. --- .../main/java/org/neo4j/driver/Values.java | 74 +++++++++++++++---- .../ObjectMappingIT.java} | 6 +- 2 files changed, 64 insertions(+), 16 deletions(-) rename driver/src/test/java/org/neo4j/driver/{ObjectMappingTests.java => integration/ObjectMappingIT.java} (98%) diff --git a/driver/src/main/java/org/neo4j/driver/Values.java b/driver/src/main/java/org/neo4j/driver/Values.java index eb7fe636f..a18899ebf 100644 --- a/driver/src/main/java/org/neo4j/driver/Values.java +++ b/driver/src/main/java/org/neo4j/driver/Values.java @@ -19,6 +19,7 @@ import static org.neo4j.driver.internal.util.Extract.assertParameter; import static org.neo4j.driver.internal.util.Iterables.newHashMapWithSize; +import java.lang.reflect.AccessibleObject; import java.time.Duration; import java.time.LocalDate; import java.time.LocalDateTime; @@ -247,6 +248,7 @@ public static Value value(Object value) { /** * Returns an array of values from object vararg. + * * @param input the object value(s) * @return the array of values */ @@ -256,6 +258,7 @@ public static Value[] values(final Object... input) { /** * Returns a value from value vararg. + * * @param input the value(s) * @return the value */ @@ -265,6 +268,7 @@ public static Value value(Value... input) { /** * Returns a value from byte vararg. + * * @param input the byte value(s) * @return the value */ @@ -274,6 +278,7 @@ public static Value value(byte... input) { /** * Returns a value from string vararg. + * * @param input the string value(s) * @return the value */ @@ -286,6 +291,7 @@ public static Value value(String... input) { /** * Returns a value from boolean vararg. + * * @param input the boolean value(s) * @return the value */ @@ -298,6 +304,7 @@ public static Value value(boolean... input) { /** * Returns a value from char vararg. + * * @param input the char value(s) * @return the value */ @@ -310,6 +317,7 @@ public static Value value(char... input) { /** * Returns a value from long vararg. + * * @param input the long value(s) * @return the value */ @@ -322,6 +330,7 @@ public static Value value(long... input) { /** * Returns a value from short vararg. + * * @param input the short value(s) * @return the value */ @@ -331,8 +340,10 @@ public static Value value(short... input) { .collect(Collectors.toCollection(() -> new ArrayList<>(input.length))); return new ListValue(values); } + /** * Returns a value from int vararg. + * * @param input the int value(s) * @return the value */ @@ -342,8 +353,10 @@ public static Value value(int... input) { .collect(Collectors.toCollection(() -> new ArrayList<>(input.length))); return new ListValue(values); } + /** * Returns a value from double vararg. + * * @param input the double value(s) * @return the value */ @@ -356,6 +369,7 @@ public static Value value(double... input) { /** * Returns a value from float vararg. + * * @param input the float value(s) * @return the value */ @@ -368,6 +382,7 @@ public static Value value(float... input) { /** * Returns a value from list of objects. + * * @param vals the list of objects * @return the value */ @@ -379,6 +394,7 @@ public static Value value(List vals) { /** * Returns a value from iterable of objects. + * * @param val the iterable of objects * @return the value */ @@ -388,6 +404,7 @@ public static Value value(Iterable val) { /** * Returns a value from iterator of objects. + * * @param val the iterator of objects * @return the value */ @@ -401,6 +418,7 @@ public static Value value(Iterator val) { /** * Returns a value from stream of objects. + * * @param stream the stream of objects * @return the value */ @@ -463,14 +481,17 @@ public static Value value(Stream stream) { * limitations on how those are supported by the database. Please read the Neo4j Cypher Manual for more up-to-date * details. For example, at the time of writing, it is not possible to store maps as properties * (see the following page). + *

+ * If accessors for record components are not accessible, the driver will try to make them accessible using + * {@link AccessibleObject#trySetAccessible()} and will emit an error if this does not succeed. * * @param record the record to map * @return the map value + * @throws ClientException when mapping fails * @see TypeSystem#MAP() * @see java.lang.Record * @see java.lang.reflect.RecordComponent * @see Property - * @throws ClientException when mapping fails * @since 5.28.5 */ @Preview(name = "Object mapping") @@ -483,11 +504,16 @@ public static Value value(java.lang.Record record) { var isVector = recordComponent.getAnnotation(org.neo4j.driver.mapping.Vector.class) != null; Value value; try { - var objectValue = recordComponent.getAccessor().invoke(record); + var accessor = recordComponent.getAccessor(); + if (!accessor.canAccess(record) && !accessor.trySetAccessible()) { + throw new IllegalStateException( + "Failed to make record component '%s' accessible".formatted(recordComponent.getName())); + } + var objectValue = accessor.invoke(record); value = (objectValue != null) ? isVector ? vector(objectValue) : value(objectValue) : null; } catch (Throwable throwable) { var message = "Failed to map '%s' property to value during mapping '%s' to map value" - .formatted(property, record.getClass().getCanonicalName()); + .formatted(property, record.getClass().getName()); throw new ClientException( GqlStatusError.UNKNOWN.getStatus(), GqlStatusError.UNKNOWN.getStatusDescription(message), @@ -505,6 +531,7 @@ public static Value value(java.lang.Record record) { /** * Returns a value from char. + * * @param val the char value * @return the value */ @@ -514,6 +541,7 @@ public static Value value(final char val) { /** * Returns a value from string. + * * @param val the string value * @return the value */ @@ -523,6 +551,7 @@ public static Value value(final String val) { /** * Returns a value from long. + * * @param val the long value * @return the value */ @@ -532,6 +561,7 @@ public static Value value(final long val) { /** * Returns a value from int. + * * @param val the int value * @return the value */ @@ -541,6 +571,7 @@ public static Value value(final int val) { /** * Returns a value from double. + * * @param val the double value * @return the value */ @@ -550,6 +581,7 @@ public static Value value(final double val) { /** * Returns a value from boolean. + * * @param val the boolean value * @return the value */ @@ -559,6 +591,7 @@ public static Value value(final boolean val) { /** * Returns a value from string to object map. + * * @param val the string to object map * @return the value */ @@ -572,6 +605,7 @@ public static Value value(final Map val) { /** * Returns a value from local date. + * * @param localDate the local date value * @return the value */ @@ -581,6 +615,7 @@ public static Value value(LocalDate localDate) { /** * Returns a value from offset time. + * * @param offsetTime the offset time value * @return the value */ @@ -590,6 +625,7 @@ public static Value value(OffsetTime offsetTime) { /** * Returns a value from local time. + * * @param localTime the local time value * @return the value */ @@ -599,6 +635,7 @@ public static Value value(LocalTime localTime) { /** * Returns a value from local date time. + * * @param localDateTime the local date time value * @return the value */ @@ -608,6 +645,7 @@ public static Value value(LocalDateTime localDateTime) { /** * Returns a value from offset date time. + * * @param offsetDateTime the offset date time value * @return the value */ @@ -617,6 +655,7 @@ public static Value value(OffsetDateTime offsetDateTime) { /** * Returns a value from zoned date time. + * * @param zonedDateTime the zoned date time value * @return the value */ @@ -626,6 +665,7 @@ public static Value value(ZonedDateTime zonedDateTime) { /** * Returns a value from period. + * * @param period the period value * @return the value */ @@ -635,6 +675,7 @@ public static Value value(Period period) { /** * Returns a value from duration. + * * @param duration the duration value * @return the value */ @@ -644,9 +685,10 @@ public static Value value(Duration duration) { /** * Returns a value from month, day, seconds and nanoseconds values. - * @param months the month value - * @param days the day value - * @param seconds the seconds value + * + * @param months the month value + * @param days the day value + * @param seconds the seconds value * @param nanoseconds the nanoseconds value * @return the value */ @@ -656,6 +698,7 @@ public static Value isoDuration(long months, long days, long seconds, int nanose /** * Returns a value from ISO duration. + * * @param duration the ISO duration value * @return the value */ @@ -665,9 +708,10 @@ private static Value value(IsoDuration duration) { /** * Returns a value from SRID, x and y values. + * * @param srid the SRID value - * @param x the x value - * @param y the y value + * @param x the x value + * @param y the y value * @return the value */ public static Value point(int srid, double x, double y) { @@ -676,6 +720,7 @@ public static Value point(int srid, double x, double y) { /** * Returns a value from point. + * * @param point the point value * @return the value */ @@ -685,10 +730,11 @@ private static Value value(Point point) { /** * Returns a value from SRID, x ,y and z values. + * * @param srid the SRID value - * @param x the x value - * @param y the y value - * @param z the z value + * @param x the x value + * @param y the y value + * @param z the z value * @return the value */ public static Value point(int srid, double x, double y, double z) { @@ -854,7 +900,7 @@ public static Function> ofMap() { * the provided converter. * * @param valueConverter converter to use for the values of the map - * @param the type of values in the returned map + * @param the type of values in the returned map * @return a function that returns {@link Value#asMap(Function)} of a {@link Value} */ public static Function> ofMap(final Function valueConverter) { @@ -873,8 +919,8 @@ public static Function ofEntity() { /** * Converts values to {@link Long entity id}. * - * @deprecated superseded by {@link #ofEntityElementId()}. * @return a function that returns the id an entity {@link Value} + * @deprecated superseded by {@link #ofEntityElementId()}. */ @Deprecated public static Function ofEntityId() { @@ -1002,7 +1048,7 @@ public static Function> ofList() { * Converts values to {@link List} of {@code T}. * * @param innerMap converter for the values inside the list - * @param the type of values inside the list + * @param the type of values inside the list * @return a function that returns {@link Value#asList(Function)} of a {@link Value} */ public static Function> ofList(final Function innerMap) { diff --git a/driver/src/test/java/org/neo4j/driver/ObjectMappingTests.java b/driver/src/test/java/org/neo4j/driver/integration/ObjectMappingIT.java similarity index 98% rename from driver/src/test/java/org/neo4j/driver/ObjectMappingTests.java rename to driver/src/test/java/org/neo4j/driver/integration/ObjectMappingIT.java index faaa251a2..d07f9bc6d 100644 --- a/driver/src/test/java/org/neo4j/driver/ObjectMappingTests.java +++ b/driver/src/test/java/org/neo4j/driver/integration/ObjectMappingIT.java @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.neo4j.driver; +package org.neo4j.driver.integration; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; @@ -38,6 +38,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.neo4j.driver.Value; +import org.neo4j.driver.Values; import org.neo4j.driver.exceptions.value.ValueException; import org.neo4j.driver.internal.InternalIsoDuration; import org.neo4j.driver.internal.InternalNode; @@ -51,7 +53,7 @@ import org.neo4j.driver.types.IsoDuration; import org.neo4j.driver.types.Point; -class ObjectMappingTests { +final class ObjectMappingIT { @ParameterizedTest @MethodSource("shouldMapValueArgs") void shouldMapValue(Function, ValueHolder> valueFunction) {