Thadir Thadir - 7 hours ago 2
Java Question

Pact with Jersey error handling

I have created this awesome controller point:

@Named
@Path("/awsome")
@Api(value = "An Awsome api")
public class AwsomeEndpoint extends BaseEndpoint {

@GET
@Path("{isThisAwsome}")
@Produces(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Get organisation by isthisAwsome",
notes = "Get AwsomeAnswer matching the given isThisAwsome.",
responseContainer = "single result",
response = JsonOrganisation.class)
@ApiResponses(value = {@ApiResponse(code = 200, message = "Awsome found"),
@ApiResponse(code = 404, message = "Awsome not found"),
@ApiResponse(code = 500, message = "Internal Error")})
public Response getAwsomeResponse(@PathParam("isThisAwsome") String isThisAwsome) {
Response response = handleErrors(() -> Response.ok(AwsomeResponseTransformer.transform(awsomeService.getAwsomeByisThisAwsome(isThisAwsome))).build());
return response;
}
}


This code uses a Lamda to handle the error flow:

public class BaseEndpoint {

protected Response handleErrors(Supplier<Response> responseSupplier) {
Response response;
try {
response = responseSupplier.get();
} catch (AwsomeRuntimeException e) {
response = createExceptionResponse(e);
}
return response;
}

private Response createExceptionResponse(AwsomeRuntimeException e) {
Response.ResponseBuilder response;

if (e.getExceptionStatus() == ExceptionStatus.NOT_FOUND) {
response = Response.status(Status.NOT_FOUND).entity(e.getMessage());
} else if (e.getExceptionStatus() == ExceptionStatus.ILLEGAL_ARGUMENT) {
response = Response.status(Status.BAD_REQUEST).entity(e.getMessage());
} else {
response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(e.getMessage());
}

return response.header("","").header("Pragma", "no-cache, no-store").header("Cache-Control", "no-cache, no-store").header("Expires", "0").build();
}}


For this I created some Pact test, all pacts work except when we i mock an error being thrown by the service we call.

An example of the pact test class:

RunWith(PactRunner.class)
@Provider("awsome")
@PactBroker(authentication=@PactBrokerAuth(username = "${pact.broker.username:x}", password = "${pact.broker.password:x}"),
protocol="${pact.broker.protocol:https}", host="${pact.broker.host:hosted.pact.dius.com.au}", port="${pact.broker.port:443}",
failIfNoPactsFound=false)
public class AccMgtPactIntegrationTest extends JerseyTest {

@TestTarget public final HttpTarget target = new HttpTarget(getPort());
private AwsomeService awsomeService;
private UserService userService;

@Override
protected ResourceConfig configure() {
awsomeService = mock(AwsomeService.class);
return new ResourceConfig().register(new OrganisationsEndpoint(awsomeService)).register(new AwsomeEndpoint())
// .property(ServerProperties.RESPONSE_SET_STATUS_OVER_SEND_ERROR, "true")
;
}

@State("Notn found")
public void whenOrganisationWithShortNameDoesNotExist() {
when(awsomeService.getOrganisationByShortName("WME")).thenThrow(new AwsomeRuntimeException(ExceptionStatus.NOT_FOUND));
} }


Now I get some weard errors from Pact, and I think its not pact thats the problem but how girlzy works in pact. Becoue I get the folowing error:

Verifying a pact between consumer and aswsome Given when
organisation with shortName does not exist Get awsome by isItAwsome
returns a response which
has status code 404 (OK)
includes headers
"Content-Type" with value "application/json" (FAILED)
has a matching body (FAILED)




Failures:


0) Get organisation by short name returns a response which includes
headers "Content-Type" with value "application/json"
Expected header 'Content-Type' to have value 'application/json' but was 'text/html;charset=ISO-8859-1'

1) Get organisation by short name returns a response which has a
matching body
comparison -> Expected a response type of 'application/json' but the actual type was 'text/html'


Now if I set the (commented out propertie)
RESPONSE_SET_STATUS_OVER_SEND_ERROR


Then pact fails like this:

Verifying a pact between consumer and aswsome
Given when organisation with shortName does not exist
Get awsome by isItAwsome:
ul 13, 2017 12:54:14 PM org.glassfish.grizzly.servlet.ServletHandler doServletService
SEVERE: service exception:
javax.servlet.ServletException: javax.servlet.ServletException: java.lang.StringIndexOutOfBoundsException: String index out of range: 0
at org.glassfish.grizzly.servlet.FilterChainImpl.doFilter(FilterChainImpl.java:151)
at org.glassfish.grizzly.servlet.FilterChainImpl.invokeFilterChain(FilterChainImpl.java:106)
at org.glassfish.grizzly.servlet.ServletHandler.doServletService(ServletHandler.java:224)
at org.glassfish.grizzly.servlet.ServletHandler.service(ServletHandler.java:173)
at org.glassfish.grizzly.http.server.HttpHandler$1.run(HttpHandler.java:224)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:593)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:573)
at java.lang.Thread.run(Thread.java:745)
Caused by: javax.servlet.ServletException: java.lang.StringIndexOutOfBoundsException: String index out of range: 0
at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:489)
at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)
at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)
at org.glassfish.grizzly.servlet.FilterChainImpl.doFilter(FilterChainImpl.java:147)
... 7 more
Caused by: java.lang.StringIndexOutOfBoundsException: String index out of range: 0
at java.lang.String.charAt(String.java:658)
at org.glassfish.grizzly.http.HttpHeader.isSpecialHeader(HttpHeader.java:925)
at org.glassfish.grizzly.http.HttpHeader.handleGetSpecialHeader(HttpHeader.java:901)
at org.glassfish.grizzly.http.HttpHeader.containsHeader(HttpHeader.java:762)
at org.glassfish.grizzly.http.server.Response.containsHeader(Response.java:1268)
at org.glassfish.grizzly.servlet.HttpServletResponseImpl.containsHeader(HttpServletResponseImpl.java:472)
at org.glassfish.jersey.servlet.internal.ResponseWriter.writeResponseStatusAndHeaders(ResponseWriter.java:159)
at org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:683)
at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:444)
at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:434)
at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:329)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)
... 12 more

returns a response which
has status code 404 (FAILED)
includes headers
"Content-Type" with value "application/json" (FAILED)
has a matching body (FAILED)




Failures:



0) Get organisation by short name returns a response which includes headers "Content-Type" with value "application/json"
Expected header 'Content-Type' to have value 'application/json' but was 'text/html;charset=ISO-8859-1'

1) Get organisation by short name returns a response which has a matching body
comparison -> Expected a response type of 'application/json' but the actual type was 'text/html'

2) Get organisation by short name returns a response which has status code

404
assert expectedStatus == actualStatus
| | |
404 | 500
false


I have the feeling that grizly does not like the Lamda I made for the error handling. But I do not want to change my code just so that I can make my provider work because of he server container. Any ideas I have been trying to debug the code and it flows correctly till it hands it over to the container. And then it seems to ignore the Jersy annotations completely

Answer

So in the end, the fix feels dirty but it worked. It seems that the grizley container doesnt realy load whole jersy. So in the end I ended up putting the JSON response in my mock like this:

when(awsomeService.getOrganisationByShortName("WME")).thenThrow(new AwsomeRuntimeException(ExceptionStatus.NOT_FOUND,"{\n"
        + "    \"timestamp\": 1500040460362,\n" + "    \"status\": 404,\n" + "    \"error\": \"Not Found\",\n" + "    \"message\": \"Not Found\",\n"
        + "    \"path\": \"/awsome/isThisAwsome/ABCDE\"\n" + "}"));

Pact seems happy its getting somting like a JSON response it can map to. And I dont have to load more Jersy then I want in the test container. (Witch I coudent figure out).

So all in all this works it doesnt feel fully the richt way but its for a non standard flow so I am ok with it for now.