Skip to content

Commit 70187e6

Browse files
author
Gael Abadin
committed
Create 6828 payara issue reproducer by adding 2 endpoints throwing EJBException wrapped and non-wrapped ValidationExceptions that should be captured by the provided custom EJBExceptionMapper and ValidationExceptionMapper
1 parent 7bcb0c6 commit 70187e6

File tree

11 files changed

+199
-9
lines changed

11 files changed

+199
-9
lines changed

hello-payara-world-ejb/src/main/java/com/example/payara/hello/HelloService.java

+6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import jakarta.ejb.Singleton;
88
import jakarta.ejb.Startup;
99
import jakarta.inject.Inject;
10+
import jakarta.validation.ValidationException;
1011

1112
@Singleton
1213
@Startup
@@ -29,4 +30,9 @@ public String hello() {
2930
return helloStorage.read(id).getMessage();
3031
}
3132

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

hello-payara-world-test/pom.xml

+6-3
Original file line numberDiff line numberDiff line change
@@ -179,8 +179,9 @@
179179
<id>pre_deploy_clean_test_environment</id>
180180
<configuration>
181181
<skip>${skip.test.deployment}</skip>
182-
<executable>docker-compose</executable>
182+
<executable>docker</executable>
183183
<arguments>
184+
<argument>compose</argument>
184185
<argument>-f</argument>
185186
<argument>target/test-classes/docker-compose.yml</argument>
186187
<argument>down</argument>
@@ -202,8 +203,9 @@
202203
<id>deploy_test_environment</id>
203204
<configuration>
204205
<skip>${skip.test.deployment}</skip>
205-
<executable>docker-compose</executable>
206+
<executable>docker</executable>
206207
<arguments>
208+
<argument>compose</argument>
207209
<argument>-f</argument>
208210
<argument>target/test-classes/docker-compose.yml</argument>
209211
<argument>up</argument>
@@ -218,8 +220,9 @@
218220
<phase>post-integration-test</phase>
219221
<id>post_undeploy_test_environment</id>
220222
<configuration>
221-
<executable>docker-compose</executable>
223+
<executable>docker</executable>
222224
<arguments>
225+
<argument>compose</argument>
223226
<argument>-f</argument>
224227
<argument>target/test-classes/docker-compose.yml</argument>
225228
<argument>down</argument>

hello-payara-world-test/src/test/java/com/example/payara/hello/test/DeploymentIT.java

+40-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.example.payara.hello.test;
22

33
import com.example.payara.hello.test.client.HelloApplicationClient;
4+
import jakarta.ws.rs.core.Response;
5+
import org.junit.jupiter.api.AfterAll;
46
import org.junit.jupiter.api.BeforeEach;
57
import org.junit.jupiter.api.Test;
68

@@ -12,6 +14,7 @@
1214
import java.util.logging.Logger;
1315
import java.util.stream.Collectors;
1416

17+
import static java.lang.Thread.sleep;
1518
import static org.junit.jupiter.api.Assertions.assertEquals;
1619

1720
class DeploymentIT {
@@ -22,21 +25,56 @@ class DeploymentIT {
2225
Properties properties = loadProperties();
2326
String payaraHost = (String) properties.get("payara.host");
2427
int payaraPort = Integer.parseInt((String) properties.get("payara.port"));
28+
int teardownDelay = Integer.parseInt((String) properties.get("payara.teardownDelay"));
29+
static int staticTeardownDelay;
2530

2631
private HelloApplicationClient client;
2732

2833
@BeforeEach
2934
public void before() {
3035

3136
client = new HelloApplicationClient("http://"+payaraHost+":"+payaraPort);
37+
staticTeardownDelay = teardownDelay;
38+
39+
}
40+
41+
@AfterAll
42+
public static void afterAll() throws InterruptedException {
43+
44+
// wait before teardown
45+
sleep(staticTeardownDelay);
3246

3347
}
3448

3549
@Test
3650
void testHello(){
3751
client.waitForServiceToBeHealthy();
38-
assertEquals("Hello, World!", client.helloWorld());
39-
assertEquals("", parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "SEVERE"));
52+
assertEquals("Hello, World!", client.helloWorld(), "Hello world endpoint response message must match.");
53+
assertEquals("", parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "SEVERE"),
54+
"There should be no SEVERE log traces in the server logs after a hello world endpoint call.");
55+
logger.info(parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "successfully deployed in"));
56+
}
57+
58+
@Test
59+
void testHelloBadRequestValidationException(){
60+
client.waitForServiceToBeHealthy();
61+
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), client.helloThrowNotWrappedStatus(), "ValidationException" +
62+
"should be mapped to a bad request response with a 400 HTTP error status response code.");
63+
assertEquals("", parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "SEVERE"),
64+
"There should be no SEVERE log traces in the server logs after a ValidationException is mapped to an" +
65+
" error response.");
66+
logger.info(parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "successfully deployed in"));
67+
}
68+
69+
@Test
70+
void testHelloBadRequestEJBExceptionWrappedValidationException(){
71+
client.waitForServiceToBeHealthy();
72+
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), client.helloThrowWrappedStatus(), "EJBException " +
73+
"wrapped ValidationException should be mapped to a bad request response with a 400 HTTP status " +
74+
"error response code.");
75+
assertEquals("", parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "SEVERE"),
76+
"There should be no SEVERE log traces in the server logs after an " +
77+
"EJBException-wrapped ValidationException is mapped to an error response.");
4078
logger.info(parseCommandOutput("docker logs test-classes-payara-deployment-test-1", "successfully deployed in"));
4179
}
4280

hello-payara-world-test/src/test/java/com/example/payara/hello/test/client/HelloApplicationClient.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ private void check(Response response) {
3636
}
3737
}
3838

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

4242
check(response);
@@ -72,4 +72,12 @@ public void waitForServiceToBeHealthy() {
7272
}
7373
throw new RuntimeException("Payara did not become healthy for base url: " + baseUrl);
7474
}
75+
76+
public int helloThrowWrappedStatus() {
77+
return client.target(apiUrl + "/hello-world/hello-throw-ejb-wrapped-validation-exception").request().get().getStatus();
78+
}
79+
80+
public int helloThrowNotWrappedStatus() {
81+
return client.target(apiUrl + "/hello-world/hello-throw-not-ejb-wrapped-validation-exception").request().get().getStatus();
82+
}
7583
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
db.port=${db.port}
22
payara.port=${payara.port}
33
payara.host=localhost
4+
payara.teardownDelay=1000

hello-payara-world-test/src/test/resources/docker-compose.yml

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
version: '3.3'
21
services:
32
db:
43
image: postgres:latest
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package com.example.payara.hello;
2+
3+
import jakarta.ejb.EJBException;
4+
import jakarta.ws.rs.core.Context;
5+
import jakarta.ws.rs.core.Response;
6+
import jakarta.ws.rs.ext.ExceptionMapper;
7+
import jakarta.ws.rs.ext.Provider;
8+
import jakarta.ws.rs.ext.Providers;
9+
10+
/**
11+
* Maps {@link EJBException} thrown by e.g. EJB services and tries to unwrap and rethrow the exception.
12+
*/
13+
@Provider
14+
public class EJBExceptionMapper implements ExceptionMapper<EJBException> {
15+
16+
@Context
17+
private Providers providers;
18+
19+
@Override
20+
public Response toResponse(final EJBException exception) {
21+
22+
Throwable handledException = exception;
23+
24+
try {
25+
unwrapEJBException(exception);
26+
} catch (Throwable e) {
27+
// Exception is unwrapped into something not EJBException related
28+
// Try to get the mapper for it
29+
handledException = e;
30+
}
31+
32+
ExceptionMapper<Throwable> mapper = providers.getExceptionMapper((Class<Throwable>) handledException.getClass());
33+
return mapper.toResponse(handledException);
34+
}
35+
36+
/**
37+
* Throws the first nested exception which isn't an {@link EJBException}.
38+
*
39+
* @param wrappingException the EJBException
40+
*/
41+
public static void unwrapEJBException(final EJBException wrappingException) throws Throwable {
42+
Throwable pE = null;
43+
Throwable cE = wrappingException;
44+
while (cE != null && cE.getCause() != pE) {
45+
if (!(cE instanceof EJBException)) {
46+
throw cE;
47+
}
48+
pE = cE;
49+
cE = cE.getCause();
50+
}
51+
if (cE != null) {
52+
throw cE;
53+
}
54+
}
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.example.payara.hello;
2+
3+
/**
4+
* The generic error response.
5+
*/
6+
public class ErrorResponse {
7+
8+
// Status code of the HTTP response
9+
private Integer code;
10+
// A human-readable description of the error
11+
private String message;
12+
13+
public ErrorResponse() {
14+
// NOOP
15+
}
16+
17+
public Integer getCode() {
18+
return code;
19+
}
20+
21+
public void setCode(Integer code) {
22+
this.code = code;
23+
}
24+
25+
public String getMessage() {
26+
return message;
27+
}
28+
29+
public void setMessage(String message) {
30+
this.message = message;
31+
}
32+
33+
}

hello-payara-world-war/src/main/java/com/example/payara/hello/HelloResource.java

+16
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.example.payara.hello;
22

33
import jakarta.inject.Inject;
4+
import jakarta.validation.ValidationException;
45
import jakarta.ws.rs.GET;
56
import jakarta.ws.rs.Path;
67
import jakarta.ws.rs.Produces;
@@ -16,4 +17,19 @@ public class HelloResource {
1617
public String hello() {
1718
return helloService.hello();
1819
}
20+
21+
@GET
22+
@Path("hello-throw-ejb-wrapped-validation-exception")
23+
@Produces("application/json")
24+
public String helloThrowWrapped() throws ValidationException {
25+
return helloService.helloThrowEJBWrappedValidationException();
26+
}
27+
28+
@GET
29+
@Path("hello-throw-not-ejb-wrapped-validation-exception")
30+
@Produces("application/json")
31+
public String helloThrowNotWrapped() {
32+
throw new ValidationException("This is handled correctly by the ValidationExceptionMapper, and 400 is " +
33+
"returned");
34+
}
1935
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.example.payara.hello;
2+
3+
import jakarta.validation.ValidationException;
4+
import jakarta.ws.rs.core.MediaType;
5+
import jakarta.ws.rs.core.Response;
6+
import jakarta.ws.rs.ext.ExceptionMapper;
7+
import jakarta.ws.rs.ext.Provider;
8+
9+
@Provider
10+
public class ValidationExceptionMapper implements ExceptionMapper<ValidationException> {
11+
12+
@Override
13+
public Response toResponse(final ValidationException exception) {
14+
var errorResponse = new ErrorResponse();
15+
errorResponse.setCode(Response.Status.BAD_REQUEST.getStatusCode());
16+
return Response.status(Response.Status.BAD_REQUEST)
17+
.entity(errorResponse)
18+
.type(MediaType.APPLICATION_JSON_TYPE).build();
19+
}
20+
21+
}

pom.xml

+12-2
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,17 @@
8686
<directory>${project.basedir}/src/test/resources</directory>
8787
</testResource>
8888
</testResources>
89-
<plugins>
90-
</plugins>
89+
<pluginManagement>
90+
<plugins>
91+
<plugin>
92+
<groupId>org.apache.maven.plugins</groupId>
93+
<artifactId>maven-ejb-plugin</artifactId>
94+
<version>3.2.1</version>
95+
<configuration>
96+
<ejbVersion>3.0</ejbVersion>
97+
</configuration>
98+
</plugin>
99+
</plugins>
100+
</pluginManagement>
91101
</build>
92102
</project>

0 commit comments

Comments
 (0)