Reputation: 545
we develop an application using Jakarta EE 10 and Payara (Glassfish) 6.x.
Using @Valid in my resource methods results in my custom validators beeing called twice.
The first time everything is injected into the Validator class as expcected. I.e. @Context private UriInfo uriInfo;
works like a charm.
But the second time the validator is called no injection happens. With CDI managed beans we just manually look them up using CDI.current().select(Service.class).get();
.
But for UriInfo I couldn't find a similar approach.
Here are the stack trace from the first and second call to the validator. I removed the parts that are the same for both stack traces to improve readability.
isValid:82, ValidNurseCallComponentIdentifier$NurseCallComponentIdentifierValidator (at.ppcssolution.domain.nursecall.api.validation)
isValid:41, ValidNurseCallComponentIdentifier$NurseCallComponentIdentifierValidator (at.ppcssolution.domain.nursecall.api.validation)
validateSingleConstraint:180, ConstraintTree (org.hibernate.validator.internal.engine.constraintvalidation)
validateConstraints:66, SimpleConstraintTree (org.hibernate.validator.internal.engine.constraintvalidation)
validateConstraints:75, ConstraintTree (org.hibernate.validator.internal.engine.constraintvalidation)
doValidateConstraint:130, MetaConstraint (org.hibernate.validator.internal.metadata.core)
validateConstraint:123, MetaConstraint (org.hibernate.validator.internal.metadata.core)
validateMetaConstraint:555, ValidatorImpl (org.hibernate.validator.internal.engine)
validateConstraintsForSingleDefaultGroupElement:518, ValidatorImpl (org.hibernate.validator.internal.engine)
validateConstraintsForDefaultGroup:488, ValidatorImpl (org.hibernate.validator.internal.engine)
validateConstraintsForCurrentGroup:450, ValidatorImpl (org.hibernate.validator.internal.engine)
validateInContext:400, ValidatorImpl (org.hibernate.validator.internal.engine)
validateCascadedAnnotatedObjectForCurrentGroup:629, ValidatorImpl (org.hibernate.validator.internal.engine)
validateCascadedConstraints:590, ValidatorImpl (org.hibernate.validator.internal.engine)
validateParametersInContext:880, ValidatorImpl (org.hibernate.validator.internal.engine)
validateParameters:283, ValidatorImpl (org.hibernate.validator.internal.engine)
validateParameters:235, ValidatorImpl (org.hibernate.validator.internal.engine)
onValidate:154, DefaultConfiguredValidator (org.glassfish.jersey.server.validation.internal)
proceed:95, ValidationInterceptorExecutor (org.glassfish.jersey.server.validation.internal)
validateResourceAndInputParams:122, DefaultConfiguredValidator (org.glassfish.jersey.server.validation.internal)
invoke:136, AbstractJavaResourceMethodDispatcher (org.glassfish.jersey.server.model.internal)
doDispatch:159, JavaResourceMethodDispatcherProvider$VoidOutInvoker (org.glassfish.jersey.server.model.internal)
dispatch:93, AbstractJavaResourceMethodDispatcher (org.glassfish.jersey.server.model.internal)
invoke:478, ResourceMethodInvoker (org.glassfish.jersey.server.model)
apply:400, ResourceMethodInvoker (org.glassfish.jersey.server.model)
apply:81, ResourceMethodInvoker (org.glassfish.jersey.server.model)
run:274, ServerRuntime$1 (org.glassfish.jersey.server)
call:248, Errors$1 (org.glassfish.jersey.internal)
call:244, Errors$1 (org.glassfish.jersey.internal)
process:292, Errors (org.glassfish.jersey.internal)
process:274, Errors (org.glassfish.jersey.internal)
process:244, Errors (org.glassfish.jersey.internal)
runInScope:266, RequestScope (org.glassfish.jersey.process.internal)
The second failing call:
isValid:66, ValidNurseCallComponentIdentifier$NurseCallComponentIdentifierValidator (at.ppcssolution.domain.nursecall.api.validation)
isValid$$super:-1, ValidNurseCallComponentIdentifier$NurseCallComponentIdentifierValidator$Proxy$_$$_WeldSubclass (at.ppcssolution.domain.nursecall.api.validation)
invoke0:-1, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:77, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:568, Method (java.lang.reflect)
proceedInternal:51, TerminalAroundInvokeInvocationContext (org.jboss.weld.interceptor.proxy)
proceed:78, AroundInvokeInvocationContext (org.jboss.weld.interceptor.proxy)
intercept:53, WebAppExceptionInterceptor (org.glassfish.jersey.ext.cdi1x.transaction.internal)
invoke:-1, GeneratedMethodAccessor416 (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:568, Method (java.lang.reflect)
invoke:73, SimpleInterceptorInvocation$SimpleMethodInvocation (org.jboss.weld.interceptor.reader)
proceedInternal:66, NonTerminalAroundInvokeInvocationContext (org.jboss.weld.interceptor.proxy)
proceed:78, AroundInvokeInvocationContext (org.jboss.weld.interceptor.proxy)
proceed:212, TransactionalInterceptorBase (org.glassfish.cdi.transaction)
transactional:115, TransactionalInterceptorRequired (org.glassfish.cdi.transaction)
invoke:-1, GeneratedMethodAccessor415 (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:568, Method (java.lang.reflect)
invoke:73, SimpleInterceptorInvocation$SimpleMethodInvocation (org.jboss.weld.interceptor.reader)
executeAroundInvoke:84, InterceptorMethodHandler (org.jboss.weld.interceptor.proxy)
executeInterception:72, InterceptorMethodHandler (org.jboss.weld.interceptor.proxy)
invoke:56, InterceptorMethodHandler (org.jboss.weld.interceptor.proxy)
invoke:79, CombinedInterceptorAndDecoratorStackMethodHandler (org.jboss.weld.bean.proxy)
invoke:68, CombinedInterceptorAndDecoratorStackMethodHandler (org.jboss.weld.bean.proxy)
isValid:-1, ValidNurseCallComponentIdentifier$NurseCallComponentIdentifierValidator$Proxy$_$$_WeldSubclass (at.ppcssolution.domain.nursecall.api.validation)
isValid:39, ValidNurseCallComponentIdentifier$NurseCallComponentIdentifierValidator (at.ppcssolution.domain.nursecall.api.validation)
validateSingleConstraint:180, ConstraintTree (org.hibernate.validator.internal.engine.constraintvalidation)
validateConstraints:66, SimpleConstraintTree (org.hibernate.validator.internal.engine.constraintvalidation)
validateConstraints:75, ConstraintTree (org.hibernate.validator.internal.engine.constraintvalidation)
doValidateConstraint:130, MetaConstraint (org.hibernate.validator.internal.metadata.core)
validateConstraint:123, MetaConstraint (org.hibernate.validator.internal.metadata.core)
validateMetaConstraint:555, ValidatorImpl (org.hibernate.validator.internal.engine)
validateConstraintsForSingleDefaultGroupElement:518, ValidatorImpl (org.hibernate.validator.internal.engine)
validateConstraintsForDefaultGroup:488, ValidatorImpl (org.hibernate.validator.internal.engine)
validateConstraintsForCurrentGroup:450, ValidatorImpl (org.hibernate.validator.internal.engine)
validateInContext:400, ValidatorImpl (org.hibernate.validator.internal.engine)
validateCascadedAnnotatedObjectForCurrentGroup:629, ValidatorImpl (org.hibernate.validator.internal.engine)
validateCascadedConstraints:590, ValidatorImpl (org.hibernate.validator.internal.engine)
validateParametersInContext:880, ValidatorImpl (org.hibernate.validator.internal.engine)
validateParameters:283, ValidatorImpl (org.hibernate.validator.internal.engine)
validateParameters:235, ValidatorImpl (org.hibernate.validator.internal.engine)
invoke0:-1, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:77, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:568, Method (java.lang.reflect)
invoke:38, AbstractBeanInstance (org.jboss.weld.bean.proxy)
invoke:106, ProxyMethodHandler (org.jboss.weld.bean.proxy)
validateParameters:-1, Validator$ExecutableValidator$1931579803$Proxy$_$$_WeldClientProxy (org.jboss.weld.generated.proxies.validation)
validateMethodInvocation:66, ValidationInterceptor (org.hibernate.validator.cdi.internal.interceptor)
invoke0:-1, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:77, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:568, Method (java.lang.reflect)
invoke:73, SimpleInterceptorInvocation$SimpleMethodInvocation (org.jboss.weld.interceptor.reader)
executeAroundInvoke:84, InterceptorMethodHandler (org.jboss.weld.interceptor.proxy)
executeInterception:72, InterceptorMethodHandler (org.jboss.weld.interceptor.proxy)
invoke:56, InterceptorMethodHandler (org.jboss.weld.interceptor.proxy)
invoke:79, CombinedInterceptorAndDecoratorStackMethodHandler (org.jboss.weld.bean.proxy)
invoke:68, CombinedInterceptorAndDecoratorStackMethodHandler (org.jboss.weld.bean.proxy)
createWallButton:-1, WallButtonResource$Proxy$_$$_WeldSubclass (at.ppcssolution.domain.nursecall.rest)
invoke0:-1, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:77, NativeMethodAccessorImpl (jdk.internal.reflect)
invoke:43, DelegatingMethodAccessorImpl (jdk.internal.reflect)
invoke:568, Method (java.lang.reflect)
lambda$static$0:52, ResourceMethodInvocationHandlerFactory (org.glassfish.jersey.server.model.internal)
invoke:-1, ResourceMethodInvocationHandlerFactory$$Lambda$1810/0x00000008016a4440 (org.glassfish.jersey.server.model.internal)
run:146, AbstractJavaResourceMethodDispatcher$1 (org.glassfish.jersey.server.model.internal)
invoke:189, AbstractJavaResourceMethodDispatcher (org.glassfish.jersey.server.model.internal)
doDispatch:159, JavaResourceMethodDispatcherProvider$VoidOutInvoker (org.glassfish.jersey.server.model.internal)
dispatch:93, AbstractJavaResourceMethodDispatcher (org.glassfish.jersey.server.model.internal)
invoke:478, ResourceMethodInvoker (org.glassfish.jersey.server.model)
apply:400, ResourceMethodInvoker (org.glassfish.jersey.server.model)
apply:81, ResourceMethodInvoker (org.glassfish.jersey.server.model)
run:274, ServerRuntime$1 (org.glassfish.jersey.server)
call:248, Errors$1 (org.glassfish.jersey.internal)
call:244, Errors$1 (org.glassfish.jersey.internal)
process:292, Errors (org.glassfish.jersey.internal)
process:274, Errors (org.glassfish.jersey.internal)
process:244, Errors (org.glassfish.jersey.internal)
runInScope:266, RequestScope (org.glassfish.jersey.process.internal)
in the build.gradle file related entries are:
providedCompile 'org.hibernate:hibernate-validator:8.0.0.Final'
providedCompile 'jakarta.validation:jakarta.validation-api:3.0.1'
providedCompile 'fish.payara.api:payara-api:6.2023.9'
providedCompile 'fish.payara.server.core.common:container-common:6.2023.8'
providedCompile 'org.eclipse.persistence:org.eclipse.persistence.jpa:4.0.1.payara-p1'
providedCompile 'org.glassfish.jersey.core:jersey-server:3.1.0.payara-p1'
providedCompile 'org.glassfish.jersey.media:jersey-media-multipart:3.1.0.payara-p1'
providedCompile 'org.glassfish.jersey.media:jersey-media-json-jackson:3.1.0.payara-p1'
The validated resource method looks like this:
@Path("/wallbuttons")
public class WallButtonResource extends SubResource {
[....]
@POST
public void createWallButton(@Valid ApiWallButton entity,
@Suspended AsyncResponse asyncResponse) {
final Long groupId = getGroupId(uriInfo);
final UriBuilder ub = uriInfo.getAbsolutePathBuilder();
final Authentication authentication = AuthenticationEvaluator.getCurrentAuthentication();
ExceptionHandlingExecutor.executeRequestWithAuthentication(executorService, () -> {
final Entity created = wallButtonService.create(entity);
asyncResponse.resume(
Response.created(
UriHelper.createURLForApiRepresentation(ub, created)).entity(created)
.build()
);
}, asyncResponse, AuthenticationEvaluator.getCurrentAuthentication());
}
And the validator
package at.ppcssolution.domain.nursecall.api.validation;
import at.ppcssolution.domain.nursecall.api.ApiWallButton;
import at.ppcssolution.domain.nursecall.api.IdentifierExtractor;
import at.ppcssolution.domain.nursecall.entity.WallButton;
import at.ppcssolution.domain.nursecall.entity.WallButton_;
import at.ppcssolution.domain.nursecall.service.WallButtonService;
import at.seres.filter.modelfilter.AttributeFilter;
import at.seres.filter.modelfilter.ModelFilter;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.spi.CDI;
import jakarta.transaction.Transactional;
import jakarta.validation.Constraint;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import jakarta.validation.Payload;
import org.hibernate.validator.constraintvalidation.HibernateConstraintValidatorContext;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
@Constraint(validatedBy = {UniqueWallButton.UniqueIdentifierValidator.class})
@Target(value = {ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface UniqueWallButton {
String message() default "{device.uniqueImei}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@ApplicationScoped
class UniqueIdentifierValidator implements ConstraintValidator<UniqueWallButton, ApiWallButton> {
private final WallButtonService wallButtonService;
@Context
private UriInfo uriInfo;
public UniqueIdentifierValidator() {
this.wallButtonService = CDI.current().select(WallButtonService.class).get();
}
@Override
public void initialize(UniqueWallButton constraintAnnotation) {
}
@Override
@Transactional
public boolean isValid(ApiWallButton value, ConstraintValidatorContext context) {
boolean hasMultipleIds = false;
if(uriInfo != null) {
final Optional<String> multipleIds = uriInfo.getQueryParameters().get("multipleIds").stream().findFirst();
hasMultipleIds = Boolean.valueOf(multipleIds.orElse("false"));
}
List<String> identifiers = new ArrayList<>();
if(hasMultipleIds) {
final Set<String> uniqueIdentifiers = IdentifierExtractor.getUniqueIdentifiers(value.getIdentifier());
identifiers.addAll(uniqueIdentifiers);
} else {
identifiers.add(value.getIdentifier());
}
List<ModelFilter> filters = new ArrayList<>();
filters.add(AttributeFilter.filter(identifiers, WallButton_.identifier));
final List<WallButton> buttonsWithSameIdentifier = wallButtonService.getAll(filters);
boolean isValid;
if (buttonsWithSameIdentifier.isEmpty()) {
return true;
} else if (buttonsWithSameIdentifier.size() == 1) {
//If it is the same button - everything is cool
isValid = Objects.equals(buttonsWithSameIdentifier.get(0).getId(), value.getId());
} else {
//more than one matching identifier -> this is definitely not valid
isValid = false;
}
if (!isValid) {
context.disableDefaultConstraintViolation();
if(hasMultipleIds) {
HibernateConstraintValidatorContext hibernateConstraintValidatorContext =
context.unwrap( HibernateConstraintValidatorContext.class );
hibernateConstraintValidatorContext.addMessageParameter("usedIdentifiers",
buttonsWithSameIdentifier.stream().map(wallButton -> wallButton.getIdentifier()).sorted().collect(Collectors.toList()).toString());
hibernateConstraintValidatorContext
.buildConstraintViolationWithTemplate("{device.uniqueIdentifiers}")
.addPropertyNode("identifier")
.addConstraintViolation();
} else {
context.buildConstraintViolationWithTemplate(context.
getDefaultConstraintMessageTemplate())
.addPropertyNode("identifier")
.addConstraintViolation();
}
}
return isValid;
}
}
}
Upvotes: 0
Views: 44