Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't fail Hibernate Validator when no RESTEasy Reactive request is in progress #19844

Merged
merged 1 commit into from
Sep 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ public ResteasyReactiveContextLocaleResolver(HttpHeaders headers) {

@Override
protected HttpHeaders getHeaders() {
return headers;
try {
headers.getLength(); // this forces the creation of the actual object which will fail if there is no request in flight
return headers;
} catch (IllegalStateException e) {
return null;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not possible to return an empty header object? I'm not sure I understand how the case of not having a request can happen and when the user will be confronted to it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I understand from the OP's case is that ResteasyReactiveRequestContext.getHttpHeaders() fails somehow. I'm wondering if we should return empty headers there and thus cover this case entirely.

Note that I have absolutely no knowledge of the RR codebase so I'm just thinking out loud, given the pattern above looks hackyish and fixing the issue at the source might be a more general solution.

Copy link
Contributor Author

@geoand geoand Sep 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

org.jboss.resteasy.reactive.server.injection.ContextProducers.getHttpHeaders() would indeed make sense to return an empty headers in this case. However it does not make sense for others in ContextProducers, so we need a consistent approach.

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ SecurityContext securityContext() {
}

private ResteasyReactiveRequestContext getContext() {
return CurrentRequestManager.get();
ResteasyReactiveRequestContext context = CurrentRequestManager.get();
if (context == null) {
throw new IllegalStateException("No RESTEasy Reactive request in progress");
}
return context;
}
}
182 changes: 182 additions & 0 deletions integration-tests/hibernate-validator-resteasy-reactive/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>quarkus-integration-tests-parent</artifactId>
<groupId>io.quarkus</groupId>
<version>999-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<artifactId>quarkus-integration-test-hibernate-validator-resteasy-reactive</artifactId>
<name>Quarkus - Integration Tests - Hibernate Validator</name>
<description>Module that contains Hibernate Validator/Bean Validation related tests using RESTEasy Reactive</description>

<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jsonb</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc</artifactId>
</dependency>

<!-- To test the ORM integration -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm</artifactId>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2</artifactId>
</dependency>

<!-- Test dependencies -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-junit5</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-test-h2</artifactId>
<scope>test</scope>
</dependency>
<!-- Minimal test dependencies to *-deployment artifacts for consistent build order -->
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-arc-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-validator-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-jdbc-h2-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-resteasy-reactive-jsonb-deployment</artifactId>
<version>${project.version}</version>
<type>pom</type>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>build</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<!-- force the locale as we want to explicitly test message interpolation -->
<user.language>en</user.language>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>

<profiles>
<profile>
<id>native-image</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<!-- add some custom config, the rest comes from parent -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<!-- force the locale as we want to explicitly test message interpolation -->
<user.language>en</user.language>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package io.quarkus.it.hibernate.validator;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import javax.enterprise.event.Observes;
import javax.inject.Inject;
import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validator;
import javax.validation.constraints.DecimalMin;
import javax.validation.constraints.Email;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.hibernate.validator.constraints.Length;

import io.quarkus.it.hibernate.validator.custom.MyOtherBean;
import io.quarkus.runtime.StartupEvent;

@Path("/hibernate-validator/test")
public class HibernateValidatorTestResource {

@Inject
Validator validator;

public void testValidationOutsideOfResteasyContext(@Observes StartupEvent startupEvent) {
validator.validate(new MyOtherBean(null));
}

@GET
@Path("/basic-features")
@Produces(MediaType.TEXT_PLAIN)
public String testBasicFeatures() {
ResultBuilder result = new ResultBuilder();

Map<String, List<String>> invalidCategorizedEmails = new HashMap<>();
invalidCategorizedEmails.put("a", Collections.singletonList("b"));

result.append(formatViolations(validator.validate(new MyBean(
"Bill Jones",
"b",
Collections.singletonList("c"),
-4d,
invalidCategorizedEmails))));

Map<String, List<String>> validCategorizedEmails = new HashMap<>();
validCategorizedEmails.put("Professional", Collections.singletonList("[email protected]"));

result.append(formatViolations(validator.validate(new MyBean(
"Bill Jones",
"[email protected]",
Collections.singletonList("[email protected]"),
5d,
validCategorizedEmails))));

return result.build();
}

private String formatViolations(Set<? extends ConstraintViolation<?>> violations) {
if (violations.isEmpty()) {
return "passed";
}

return "failed: " + violations.stream()
.map(v -> v.getPropertyPath().toString() + " (" + v.getMessage() + ")")
.sorted()
.collect(Collectors.joining(", "));
}

public static class MyBean {

private String name;

@Email
private String email;

private List<@Email String> additionalEmails;

@DecimalMin("0")
private Double score;

private Map<@Length(min = 3) String, List<@Email String>> categorizedEmails;

@Valid
private NestedBeanWithoutConstraints nestedBeanWithoutConstraints;

public MyBean(String name, String email, List<String> additionalEmails, Double score,
Map<String, List<String>> categorizedEmails) {
this.name = name;
this.email = email;
this.additionalEmails = additionalEmails;
this.score = score;
this.categorizedEmails = categorizedEmails;
this.nestedBeanWithoutConstraints = new NestedBeanWithoutConstraints();
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public List<String> getAdditionalEmails() {
return additionalEmails;
}

public void setAdditionalEmails(List<String> additionalEmails) {
this.additionalEmails = additionalEmails;
}

public Double getScore() {
return score;
}

public void setScore(Double score) {
this.score = score;
}

public Map<String, List<String>> getCategorizedEmails() {
return categorizedEmails;
}

public void setCategorizedEmails(Map<String, List<String>> categorizedEmails) {
this.categorizedEmails = categorizedEmails;
}
}

private static class ResultBuilder {

private StringBuilder builder = new StringBuilder();

public ResultBuilder append(String element) {
if (builder.length() > 0) {
builder.append("\n");
}
builder.append(element);
return this;
}

public String build() {
return builder.toString();
}
}

private static class NestedBeanWithoutConstraints {

@SuppressWarnings("unused")
private String property;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package io.quarkus.it.hibernate.validator;

import javax.validation.constraints.Digits;

public interface HibernateValidatorTestResourceGenericInterface<T extends Number> {

T testRestEndpointGenericMethodValidation(@Digits(integer = 5, fraction = 0) T id);

}
Loading