Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
Re: [bean-validation-dev] Provides a way to implement a dynamic clock provider?

Hello,

Maybe you could provide a bit more information on your particular use case first?

> solution that dynamically decides which Clock to provide based on the object currently being validated

Now, as to the suggested interfaces. `ClockProvider` is exposed to the user through `ConstraintValidatorContext` in `ConstraintValidator#isValid(..)`, which means that at this point, it was already decided that the constraint validator has to be executed (i.e. groups were already evaluated, and constraints relevant for those groups already picked, so you may or may not have access to groups at this stage). The same applies to the field/method parameters. On top of that, validation providers may rely on other means than field/method reflection types. Hence, I'd suggest avoiding reflection types in the API if possible.

I'm also unaware of such a feature in any existing validation providers, so it may make sense to introduce something (if anything at all) there first as an incubating feature, validate the assumptions, get some user feedback and iterate on it before introducing the spec change.

Have a nice day,
Marko

On Tue, 15 Oct 2024 at 16:41, Xi Minghui via bean-validation-dev <bean-validation-dev@xxxxxxxxxxx> wrote:

Sorry for my negligence, just having the validation object is not enough, more information is needed:

package jakarta.validation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.time.Clock;
public interface DynamicClockProvider {
    /**
     * Returns the clock which serves as the reference for {@code now}.
     * <p>
     * Ensure that the {@link Clock} used as the reference for {@code now} is obtained
     * for each verification of {@code @Future}, {@code @FutureOrPresent}, {@code @Past},
     * and {@code @PastOrPresent} constraints.
     *
     * @param fieldHolder if the validation object is a field value of a class, then this
     *                    will be the instance that holds the value of this field
     * @param field if the validation object is a field value of a class, then this will
     *              be the class's field reflection object
     * @param method if the validation object is a method parameter or return, then this
     *               will be the reflection object of this method
     * @param object object to validate
     * @param groups the group or list of groups targeted for validation (defaults to
     *        {@link jakarta.validation.groups.Default Default})
     * @param <T> the type of the object to validate
     * @return the clock which serves as the reference for {@code now}; must not be
     * {@code null}
     *
     * @since 4.0
     */
    <T> Clock getClock(Object fieldHolder,
                       Field field,
                       Method method,
                       T object,
                       Class<?>... groups);
}


Kind regards,
Xi Minghui

On 10/15/2024 9:30 PM, Xi Minghui wrote:
Hi,

I'm trying to implement a solution that dynamically decides which Clock to provide based on the object currently being validated, but the current ClockProvider contract doesn't support this.

I thought about implementing it through the ThreadLocal mechanism, but this would mean that validation relies on threads, and to support asynchronous or concurrent execution, more work would be required, which would increase complexity. I believe the most direct, effective, and simple approach is to pass the current validation object to the ClockProvider, like: change java.time.Clock getClock() to <T> java.time.Clock getClock(T object, Class<?>... groups).


Points to consider for the change:

- Keep functional interface: Ideally, ClockProvider should still be usable with lambda expressions.
- Consider backward compatibility: Think about the pain of code migration, especially with lambda method references.


Proposed Approach 1: Add a New Interface

package jakarta.validation;
import java.time.Clock;
public interface DynamicClockProvider {
    /**
     * Returns the clock which serves as the reference for {@code now}.
     * <p>
     * Ensure that the {@link Clock} used as the reference for {@code now} is obtained
     * for each verification of {@code @Future}, {@code @FutureOrPresent}, {@code @Past},
     * and {@code @PastOrPresent} constraints.
     *
     * @param object object to validate
     * @param groups the group or list of groups targeted for validation (defaults to
     *        {@link jakarta.validation.groups.Default Default})
     * @param <T> the type of the object to validate
     * @return the clock which serves as the reference for {@code now}; must not be
     * {@code null}
     *
     * @since 4.0
     */
    <T> Clock getClock(T object, Class<?>... groups);
}


Proposed Approach 2: Update the ClockProvider

/*
 * Jakarta Validation API
 *
 * License: Apache License, Version 2.0
 * See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
 */
package jakarta.validation;
import java.time.Clock;
/**
 * Contract for obtaining the {@link Clock} used as the reference for {@code now} when
 * validating the {@code @Future}, {@code FutureOrPresent}, {@code Past}, and
 * {@code @PastOrPresent} constraints.
 * <p>
 * The default implementation will return the current system time. Plugging in custom
 * implementations may be useful for instance in batch applications which need to run with a
 * specific logical date, e.g. with yesterday's date when re-running a failed batch job
 * execution.
 * <p>
 * Implementations must be safe for access from several threads at the same time.
 *
 * @author Gunnar Morling
 * @author Guillaume Smet
 * @since 2.0
 */
public interface ClockProvider {
    /**
     * Returns the clock which serves as the reference for {@code now}.
     * <p>
     * Ensure that the {@link Clock} used as the reference for {@code now} is obtained
     * for each verification of {@code @Future}, {@code @FutureOrPresent}, {@code @Past},
     * and {@code @PastOrPresent} constraints.
     *
     * @param object object to validate
     * @param groups the group or list of groups targeted for validation (defaults to
     *        {@link jakarta.validation.groups.Default Default})
     * @param <T> the type of the object to validate
     * @return the clock which serves as the reference for {@code now}; must not be
     * {@code null}
     *
     * @since 4.0
     */
    <T> Clock getClock(T object, Class<?>... groups);
}


What do you think of this? I would greatly appreciate your feedback and any other ideas you might have.

Kind regards,
Xi Minghui

_______________________________________________
bean-validation-dev mailing list
bean-validation-dev@xxxxxxxxxxx
To unsubscribe from this list, visit https://www.eclipse.org/mailman/listinfo/bean-validation-dev

Back to the top