Skip to content

Commit

Permalink
Create 6828 payara issue reproducer by adding 2 endpoints throwing EJ…
Browse files Browse the repository at this point in the history
…BException wrapped and non-wrapped ValidationExceptions that should be captured by the provided custom EJBExceptionMapper and ValidationExceptionMapper
  • Loading branch information
Gael Abadin committed Jul 19, 2024
1 parent 7bcb0c6 commit 70187e6
Show file tree
Hide file tree
Showing 11 changed files with 199 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import jakarta.ejb.Singleton;
import jakarta.ejb.Startup;
import jakarta.inject.Inject;
import jakarta.validation.ValidationException;

@Singleton
@Startup
Expand All @@ -29,4 +30,9 @@ public String hello() {
return helloStorage.read(id).getMessage();
}

public String helloThrowEJBWrappedValidationException() throws ValidationException {
throw new ValidationException("This should become a 400 after the CustomValidationExceptionMapper maps " +
"this exception that the EJBExceptionMapper rethrows after unwrapping the EJBException containing it");
}

}
9 changes: 6 additions & 3 deletions hello-payara-world-test/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -179,8 +179,9 @@
<id>pre_deploy_clean_test_environment</id>
<configuration>
<skip>${skip.test.deployment}</skip>
<executable>docker-compose</executable>
<executable>docker</executable>
<arguments>
<argument>compose</argument>
<argument>-f</argument>
<argument>target/test-classes/docker-compose.yml</argument>
<argument>down</argument>
Expand All @@ -202,8 +203,9 @@
<id>deploy_test_environment</id>
<configuration>
<skip>${skip.test.deployment}</skip>
<executable>docker-compose</executable>
<executable>docker</executable>
<arguments>
<argument>compose</argument>
<argument>-f</argument>
<argument>target/test-classes/docker-compose.yml</argument>
<argument>up</argument>
Expand All @@ -218,8 +220,9 @@
<phase>post-integration-test</phase>
<id>post_undeploy_test_environment</id>
<configuration>
<executable>docker-compose</executable>
<executable>docker</executable>
<arguments>
<argument>compose</argument>
<argument>-f</argument>
<argument>target/test-classes/docker-compose.yml</argument>
<argument>down</argument>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.example.payara.hello.test;

import com.example.payara.hello.test.client.HelloApplicationClient;
import jakarta.ws.rs.core.Response;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

Expand All @@ -12,6 +14,7 @@
import java.util.logging.Logger;
import java.util.stream.Collectors;

import static java.lang.Thread.sleep;
import static org.junit.jupiter.api.Assertions.assertEquals;

class DeploymentIT {
Expand All @@ -22,21 +25,56 @@ class DeploymentIT {
Properties properties = loadProperties();
String payaraHost = (String) properties.get("payara.host");
int payaraPort = Integer.parseInt((String) properties.get("payara.port"));
int teardownDelay = Integer.parseInt((String) properties.get("payara.teardownDelay"));
static int staticTeardownDelay;

private HelloApplicationClient client;

@BeforeEach
public void before() {

client = new HelloApplicationClient("http://"+payaraHost+":"+payaraPort);
staticTeardownDelay = teardownDelay;

}

@AfterAll
public static void afterAll() throws InterruptedException {

// wait before teardown
sleep(staticTeardownDelay);

}

@Test
void testHello(){
client.waitForServiceToBeHealthy();
assertEquals("Hello, World!", client.helloWorld());
assertEquals("", parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "SEVERE"));
assertEquals("Hello, World!", client.helloWorld(), "Hello world endpoint response message must match.");
assertEquals("", parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "SEVERE"),
"There should be no SEVERE log traces in the server logs after a hello world endpoint call.");
logger.info(parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "successfully deployed in"));
}

@Test
void testHelloBadRequestValidationException(){
client.waitForServiceToBeHealthy();
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), client.helloThrowNotWrappedStatus(), "ValidationException" +
"should be mapped to a bad request response with a 400 HTTP error status response code.");
assertEquals("", parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "SEVERE"),
"There should be no SEVERE log traces in the server logs after a ValidationException is mapped to an" +
" error response.");
logger.info(parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "successfully deployed in"));
}

@Test
void testHelloBadRequestEJBExceptionWrappedValidationException(){
client.waitForServiceToBeHealthy();
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), client.helloThrowWrappedStatus(), "EJBException " +
"wrapped ValidationException should be mapped to a bad request response with a 400 HTTP status " +
"error response code.");
assertEquals("", parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "SEVERE"),
"There should be no SEVERE log traces in the server logs after an " +
"EJBException-wrapped ValidationException is mapped to an error response.");
logger.info(parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "successfully deployed in"));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ private void check(Response response) {
}
}

public String helloWorld() {
public String helloWorld() {
Response response = client.target(apiUrl + "/hello-world").request().get();

check(response);
Expand Down Expand Up @@ -72,4 +72,12 @@ public void waitForServiceToBeHealthy() {
}
throw new RuntimeException("Payara did not become healthy for base url: " + baseUrl);
}

public int helloThrowWrappedStatus() {
return client.target(apiUrl + "/hello-world/hello-throw-ejb-wrapped-validation-exception").request().get().getStatus();
}

public int helloThrowNotWrappedStatus() {
return client.target(apiUrl + "/hello-world/hello-throw-not-ejb-wrapped-validation-exception").request().get().getStatus();
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
db.port=${db.port}
payara.port=${payara.port}
payara.host=localhost
payara.teardownDelay=1000
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
version: '3.3'
services:
db:
image: postgres:latest
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.example.payara.hello;

import jakarta.ejb.EJBException;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
import jakarta.ws.rs.ext.Providers;

/**
* Maps {@link EJBException} thrown by e.g. EJB services and tries to unwrap and rethrow the exception.
*/
@Provider
public class EJBExceptionMapper implements ExceptionMapper<EJBException> {

@Context
private Providers providers;

@Override
public Response toResponse(final EJBException exception) {

Throwable handledException = exception;

try {
unwrapEJBException(exception);
} catch (Throwable e) {
// Exception is unwrapped into something not EJBException related
// Try to get the mapper for it
handledException = e;
}

ExceptionMapper<Throwable> mapper = providers.getExceptionMapper((Class<Throwable>) handledException.getClass());
return mapper.toResponse(handledException);
}

/**
* Throws the first nested exception which isn't an {@link EJBException}.
*
* @param wrappingException the EJBException
*/
public static void unwrapEJBException(final EJBException wrappingException) throws Throwable {
Throwable pE = null;
Throwable cE = wrappingException;
while (cE != null && cE.getCause() != pE) {
if (!(cE instanceof EJBException)) {
throw cE;
}
pE = cE;
cE = cE.getCause();
}
if (cE != null) {
throw cE;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.example.payara.hello;

/**
* The generic error response.
*/
public class ErrorResponse {

// Status code of the HTTP response
private Integer code;
// A human-readable description of the error
private String message;

public ErrorResponse() {
// NOOP
}

public Integer getCode() {
return code;
}

public void setCode(Integer code) {
this.code = code;
}

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.example.payara.hello;

import jakarta.inject.Inject;
import jakarta.validation.ValidationException;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
Expand All @@ -16,4 +17,19 @@ public class HelloResource {
public String hello() {
return helloService.hello();
}

@GET
@Path("hello-throw-ejb-wrapped-validation-exception")
@Produces("application/json")
public String helloThrowWrapped() throws ValidationException {
return helloService.helloThrowEJBWrappedValidationException();
}

@GET
@Path("hello-throw-not-ejb-wrapped-validation-exception")
@Produces("application/json")
public String helloThrowNotWrapped() {
throw new ValidationException("This is handled correctly by the ValidationExceptionMapper, and 400 is " +
"returned");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example.payara.hello;

import jakarta.validation.ValidationException;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;

@Provider
public class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {

@Override
public Response toResponse(final ValidationException exception) {
var errorResponse = new ErrorResponse();
errorResponse.setCode(Response.Status.BAD_REQUEST.getStatusCode());
return Response.status(Response.Status.BAD_REQUEST)
.entity(errorResponse)
.type(MediaType.APPLICATION_JSON_TYPE).build();
}

}
14 changes: 12 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,17 @@
<directory>${project.basedir}/src/test/resources</directory>
</testResource>
</testResources>
<plugins>
</plugins>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<version>3.2.1</version>
<configuration>
<ejbVersion>3.0</ejbVersion>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>

0 comments on commit 70187e6

Please sign in to comment.