Skip to content

Fix #398: Add portable ConstraintViolationExceptionMapper for MVC validation on OpenLiberty#399

Draft
ivargrimstad wants to merge 3 commits intoeclipse-ee4j:mainfrom
ivargrimstad:copilot/398-fix
Draft

Fix #398: Add portable ConstraintViolationExceptionMapper for MVC validation on OpenLiberty#399
ivargrimstad wants to merge 3 commits intoeclipse-ee4j:mainfrom
ivargrimstad:copilot/398-fix

Conversation

@ivargrimstad
Copy link
Copy Markdown
Member

@ivargrimstad ivargrimstad commented Apr 20, 2026

Summary

Fixes #398

On OpenLiberty (and other JAX-RS runtimes that don't have a Krazo-specific mechanism to suppress built-in bean validation), the JAX-RS runtime validates controller method parameters and throws ConstraintViolationException before Krazo's CDI ValidationInterceptor can run. This results in a 400 Bad Request response instead of populating BindingResult and invoking the controller.

Jersey and RESTEasy have runtime-specific hooks (KrazoValidationInterceptor and KrazoGeneralValidator) that suppress this premature validation, but there was no equivalent for OpenLiberty/CXF.

Solution

This PR adds a portable ExceptionMapper<ConstraintViolationException> in the core module that:

  1. Detects MVC controller requests via ResourceInfo and @Controller annotation
  2. Populates BindingResult with @MvcBinding-annotated constraint violations (reusing the same logic as ValidationInterceptor)
  3. Invokes the controller method via CDI so the controller can check BindingResult.isFailed() and return the appropriate view
  4. Skips re-validation in the CDI ValidationInterceptor via a validationPerformedExternally flag on BindingResultImpl
  5. Returns the controller's response with a Viewable entity for view rendering, or handles redirects

For non-MVC requests, the mapper returns a standard 400 Bad Request response (same as the default JAX-RS behavior).

Files Changed

  • ConstraintViolationExceptionMapper.java (new) — The core ExceptionMapper
  • BindingResultImpl.java — Added validationPerformedExternally flag
  • ValidationInterceptor.java — Skips validation when flag is set
  • DefaultConfigProvider.java — Registers the new ExceptionMapper

Limitations

  • The controller method is invoked with default parameter values (nulls/zeros) since the original JAX-RS-bound values aren't accessible from the ExceptionMapper. Controllers should check BindingResult.isFailed() before using parameter values, which is the expected MVC pattern.
  • This mapper is a safety net — on Jersey and RESTEasy, the existing runtime-specific interceptors prevent the exception from being thrown in the first place.

Copilot AI and others added 2 commits April 20, 2026 08:10
…ndling (eclipse-ee4j#398)

On JAX-RS runtimes without a Krazo-specific mechanism to suppress
built-in bean validation for MVC controllers (such as OpenLiberty/CXF),
the runtime throws ConstraintViolationException before Krazo's CDI
ValidationInterceptor can run. This mapper catches the exception,
populates BindingResult, and invokes the controller via CDI so it
can handle validation errors.

Changes:
- New ConstraintViolationExceptionMapper in core module
- BindingResultImpl: add validationPerformedExternally flag
- ValidationInterceptor: skip re-validation when flag is set
- DefaultConfigProvider: register the new ExceptionMapper

Agent-Logs-Url: https://github.com/ivargrimstad/krazo/sessions/4879b273-1607-488f-be1e-c18610e7c87c

Co-authored-by: ivargrimstad <149188+ivargrimstad@users.noreply.github.com>
@ivargrimstad ivargrimstad changed the title Copilot/398 fix Fix #398: Add portable ConstraintViolationExceptionMapper for MVC validation on OpenLiberty Apr 20, 2026
@ivargrimstad ivargrimstad self-assigned this Apr 20, 2026
@mthmulders
Copy link
Copy Markdown
Contributor

mthmulders commented Apr 23, 2026

@ivargrimstad Would you like me to try and build this branch locally to see if it works?

Yes, that would be awesome!

I must admit I let GitHub CoPilot do all the work and I just haven't had time to test it yet since I am a little busy at OCX this week.

@mthmulders
Copy link
Copy Markdown
Contributor

mthmulders commented Apr 23, 2026

Tried building locally but ran into this error:

[ERROR] 'dependencies.dependency.version' for org.jboss.shrinkwrap.resolver:shrinkwrap-resolver-depchain:pom is missing. @ line 57, column 21

I see Jenkins failed with the same error. @ivargrimstad, does that make any bell ring, maybe?

Update: as a work-around, I set tck.version in the project root POM to "3.0.0".

@mthmulders
Copy link
Copy Markdown
Contributor

I've tried it on the attached reproducer project. The ConstraintViolationExceptionMapper gets invoked at the expected moment. However, it fails to invoke the MVC controller because of an ResteasyViolationExceptionImpl that happens at line 138 (at CdiUtils.newBean(CdiUtils.getApplicationBeanManager(), resourceClass)).

Here is the full stack trace:

 	at org.jboss.resteasy.plugins.validation.GeneralValidatorImpl.checkViolationsfromCDI(GeneralValidatorImpl.java:172)
 	at [internal classes]
 	at org.jboss.weld.bean.ManagedBean.create(ManagedBean.java:161)
 	at org.jboss.weld.contexts.unbound.DependentContextImpl.get(DependentContextImpl.java:64)
 	at org.jboss.weld.bean.ContextualInstanceStrategy$DefaultContextualInstanceStrategy.get(ContextualInstanceStrategy.java:100)
 	at org.jboss.weld.bean.ContextualInstance.get(ContextualInstance.java:50)
 	at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:679)
 	at org.jboss.weld.manager.BeanManagerImpl.getReference(BeanManagerImpl.java:702)
 	at org.eclipse.krazo.util.CdiUtils.newBean(CdiUtils.java:72)
 	at org.eclipse.krazo.binding.ConstraintViolationExceptionMapper.handleMvcController(ConstraintViolationExceptionMapper.java:138)
 	at org.eclipse.krazo.binding.ConstraintViolationExceptionMapper.toResponse(ConstraintViolationExceptionMapper.java:95)
 	at org.eclipse.krazo.binding.ConstraintViolationExceptionMapper.toResponse(ConstraintViolationExceptionMapper.java:60)
 	at org.jboss.resteasy.core.ExceptionHandler.executeExceptionMapper(ExceptionHandler.java:139)
 	at [internal classes]
 	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:240)
 	at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:154)
 	at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:385)
 	at [internal classes]
 	at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
 	at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1262)
 	at [internal classes]
 	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1090)
 	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:614)
 	at java.base/java.lang.Thread.run(Thread.java:1516)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bean validation doesn't work on OpenLiberty

3 participants