Non-blocking asynchronous Jersey JAX-RS with CompletableFuture - java

I'm studying Jersey and I saw in a book you can use CompletableFuture (and CompletitionStage) in order to call your API in a NON-Blocking IO way.
But when I call the API with Postman, I always get 500.
If I debug the code I see that the methods are invoked correctly.
The first GET method is synchronous and works correcly.
The second and the third return error 500.
What am i missing?
#Path("/hello")
public class HelloController {
#GET
#Path("/first")
#Produces(MediaType.TEXT_PLAIN)
public String first() {
return "It works";
}
#GET
#Path("/second")
#Produces(MediaType.TEXT_PLAIN)
public CompletionStage<Response> second() {
return CompletableFuture.supplyAsync(() -> Response.accepted().entity("Hello!").build());
}
#GET
#Path("/third")
#Produces(MediaType.TEXT_PLAIN)
public CompletableFuture<Response> third() {
return CompletableFuture.supplyAsync(() -> Response.accepted().entity("Hello!").build());
}
}

This is the correct way to create an async endpoint with Jersey:
#POST
#Path("/goes")
#Consumes("application/json")
#Produces("application/json")
public void createTodoAsync3(#Suspended final AsyncResponse asyncResponse, Todo todo) {
CompletableFuture<Response> future = CompletableFuture.supplyAsync(() -> createTodo3(todo));
future.thenAccept(resp -> asyncResponse.resume(resp));
}
private Response createTodo3(Todo todo) {
//all logic goes here
return Response.accepted().entity(todo).build();
}

Related

Reactive Mailer - blocking operation on a IO thread

With reactive mailer I am trying to persist if the email was succeeded or not.
Here down the code snippet that is not woking:
#Path("atendimentos")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class AtendimentoResource {
#Inject
AtendimentoHistoricoRepository atendimentoHistoricoRepository;
#Inject
ReactiveMailer mailer;
#GET
public Response findAll(#QueryParam("nome") String nome) {
AtendimentoHistorico atendimentoHistorico = new AtendimentoHistorico();
mailer.send(email).subscribe().with(success -> {
atendimentoHistorico.setEmailEnviado(true);
atendimentoHistoricoRepository.persist(atendimentoHistorico);
}, error -> {
});
}
}
Here is the thrown exception:
You have attempted to perform a blocking operation on a IO thread. This is not allowed, as blocking the IO thread will cause major performance issues with your application. If you want to perform blocking EntityManager operations make sure you are doing it from a worker thread.
If you want to block, you should use io.quarkus.mailer.Mailer instead of ReactiveMailer.
#Path("atendimentos")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class AtendimentoResource {
#Inject
AtendimentoHistoricoRepository atendimentoHistoricoRepository;
#Inject
Mailer mailer;
#GET
public Response findAll(#QueryParam("nome") String nome) {
AtendimentoHistorico atendimentoHistorico = new AtendimentoHistorico();
mailer.send(email);
atendimentoHistorico.setEmailEnviado(true);
atendimentoHistoricoRepository.persist(atendimentoHistorico);
}
}

How to define and test a DELETE operation endpoint using #QueryParam with Jersey?

Can I use #QueryParam when defining a DELETE operation? If yes, when I need to test it using jersey client, how the java code will look like? I tried something like
String result = client.target(url)
.queryParam("id",3)
.request(MediaType.APPLICATION_XML)
.delete(String.class);
But it was not working.
You can define a DELETE endpoint using either #PathParam (recommended) or #QueryParam
With #PathParam (recommended)
Endpoint: url/:id
#DELETE
#Path("{id}")
#Produces(MediaType.APPLICATION_JSON)
public void deletePathParam(#PathParam("id") int id) {
...
}
Test:
#Test
public void testDeletePathParam() {
Response output = target(url+"/3").request().delete();
// asserts
}
With #QueryParam (bad RESTapi design)
Endpoint: url?id=:id
#DELETE
#Produces(MediaType.APPLICATION_JSON)
public void deleteQueryParam(#QueryParam("id") int id) {
...
}
Test:
#Test
public void testDeleteQueryParam() {
Response output = target(url).queryParam("id", 3).request().delete();
// asserts
}

URI Recognition in Java Using JAX-RS #Path Annotations at Class and Method Level

I've created a situation in a Resource class where I'm receiving 404s for all 3 endpoints and was hoping someone could help explain why. Here is my resource class:
#Path("/incidents")
public class RegistrationResource {
#GET
#Path("/{incidentId}/registration")
public Response getRegisteredIncidents(#PathParam("incidentId") String incidentId) {
...
}
#GET
#Path("/registration/download")
public Response downloadRegistrationIncidentsReport() {
...
}
#GET
#Path("/registration/email")
public Response emailRegistrationIncidentsReport() {
...
}
}
I'm also using the JAX-RS Application class to register my resources and set a base "context" path across all of the endpoints:
#ApplicationPath("/context")
public class AdminApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<Class<?>>();
resources.add(RegistrationResource.class);
...
return resources;
}
}
Now if I split these methods into more than one resource class I can get each service to work fine, but my question is why won't the design above work? The full endpoints I'm trying to call here in order of the methods above are:
/context/incidents/55/registration
/context/incidents/registration/download
/context/incidents/registration/email
Why are the mappings above not being found? With this design I'm getting a 404 for each of the endpoints.
org.apache.wink.server.internal.RequestProcessor logException The following error occurred during the invocation of the handlers chain: WebApplicationException (404 - Not Found) with message 'null' while processing GET request sent to http://localhost:9081/someapp/context/incidents/55/registration
As a further point of clarification I'm using Wink 1.1 as my JAX-RS implementation since I'm bound to the packaged version that comes with WebSphere 8.5.5.5 at this time.
Thanks for your time!
-----------UPDATE #1-----------
In light of the comments below I have modified some of the endpoints. However I am still receiving random failures on certain endpoints with either 404 or, interestingly, 405 errors. It's important to note that if I redeploy sometimes different endpoints will fail, or perhaps all will work. Then if I redeploy again I will get similar, but different results of certain endpoints failing or all working. The bottom line is that it is very inconsistent.
Because of that it seems as if more endpoints are conflicting than in my original post so I am pasting all of my endpoints here for the sake of completeness.
/* Public Service Set */
#ApplicationPath("/public")
public class PublicApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<Class<?>>();
resources.add(IncidentResource.class);
resources.add(IncidentDetailsResource.class);
resources.add(TicketResource.class);
resources.add(RegistrationResource.class);
return resources;
}
}
#Path("/incidents")
public class IncidentResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidents() throws Exception {
//...
}
#Path("/")
public class IncidentDetailsResource {
#GET
#Path("/v1/incidents/{incidentId}/details")
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidentDetails(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
#GET
#Path("/v2/incidents/{incidentId}/details")
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidentDetailsAsJson(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
}
#Path("/incidents/{incidentId}/tickets")
public class TicketResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getTickets(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
}
#Path("/incidents")
public class RegistrationResource {
#POST
#Path("/{incidentId}/registration")
#Consumes(MediaType.APPLICATION_JSON)
public Response register(#PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentRegistrationRequest> baseRequestMessage) throws Exception {
//...
}
}
/* Admin Service Set */
#ApplicationPath("/admin")
public class AdminApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<Class<?>>();
resources.add(AdminIncidentResource.class);
resources.add(AdminIncidentDetailsResource.class);
resources.add(AdminRegistrationResource.class);
resources.add(AdminRegistrationReportResource.class);
resources.add(AdminTicketResource.class);
return resources;
}
}
#Path("/incidents")
public class AdminIncidentResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response addIncident(BaseRequestMessage<IncidentRequest> baseRequestMessage) throws Exception {
//...
}
#PUT
#Path("/{incidentId}")
#Produces(MediaType.APPLICATION_JSON)
public Response updateIncident(#PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentRequest> baseRequestMessage) throws Exception {
//...
}
#DELETE
#Path("/{incidentId}")
#Produces(MediaType.APPLICATION_JSON)
public Response deleteIncident(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
}
#Path("/")
public class AdminIncidentDetailsResource {
#PUT
#Path("/v1/incidents/{incidentId}/details")
#Consumes(MediaType.APPLICATION_JSON)
public Response updateIncidentDetails(#PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentDetailRequest> baseRequestMessage) throws Exception {
//...
}
#PUT
#Path("/v2/incidents/{incidentId}/details")
#Consumes(MediaType.APPLICATION_JSON)
public Response updateIncidentDetailsAsJson(#PathParam("incidentId") String incidentId, BaseRequestMessage<JsonNode> baseRequestMessage) throws Exception {
//...
}
}
#Path("/incidents/{incidentId}/tickets")
public class AdminTicketResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response addTicket(#PathParam("incidentId") String incidentId, BaseRequestMessage<TicketRequest> baseRequestMessage) throws Exception {
//...
}
#PUT
#Consumes(MediaType.APPLICATION_JSON)
public Response updateTicket(#PathParam("incidentId") String incidentId, BaseRequestMessage<TicketRequest> baseRequestMessage) throws Exception {
//...
}
#DELETE
#Path("/{detailsUrn}")
#Consumes(MediaType.APPLICATION_JSON)
public Response removeTicket(#PathParam("incidentId") String incidentId, #PathParam("detailsUrn") String detailsUrn) throws Exception {
//...
}
}
#Path("/incidents/registration/report")
public class AdminRegistrationReportResource {
#GET
#Path("/download")
#Produces("application/vnd.ms-excel")
public Response downloadRegistrationReport() throws Exception {
//...
}
#GET
#Path("/email")
#Produces(MediaType.APPLICATION_JSON)
public Response emailRegistrationReport() throws Exception {
//...
}
}
#Path("/incidents/{incidentId}/registration")
public class AdminRegistrationResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getRegisteredUsers(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
#PUT
#Consumes(MediaType.APPLICATION_JSON)
public Response updateRegisteredUser(#PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentRegistrationRequest> baseRequestMessage) throws Exception {
//...
}
}
For sake of expediency...the code above ends up generating these uris:
GET /public/incidents
GET /public/v1/incidents/{incidentId}/details
GET /public/v2/incidents/{incidentId}/details
GET /public/incidents/{incidentId}/tickets
POST /public/incidents/{incidentId}/registration
POST /admin/incidents
PUT /admin/incidents/{incidentId}
DELETE /admin/incidents/{incidentId}
PUT /admin/v1/incidents/{incidentId}/details
PUT /admin/v2/incidents/{incidentId}/details
POST /admin/incidents/{incidentId}/tickets
PUT /admin/incidents/{incidentId}/tickets
DELETE /admin/incidents/{incidentId}/tickets/{detailsUrn}
GET /admin/incidents/registration/report/download
GET /admin/incidents/registration/report/email
GET /admin/incidents/{incidentId}/registration
PUT /admin/incidents/{incidentId}/registration
That's a lot of code; I wanted to avoid that originally but it seems it is necessary. Also, for what it is worth, things seemed to be working until I added the AdminRegistrationResource class. Perhaps something with one of the endpoints in that class began causing a conflict?
Again...thanks for your time!
-----------UPDATE #2-----------
I'd thought I'd post one of my recent specific errors here in the hopes that it may help in solving the issue. When I call this endpoint:
GET /public/incidents
I'm receiving this error message:
00000203 ResourceRegis I org.apache.wink.server.internal.registry.ResourceRegistry filterDispatchMethods The system cannot find any method in the com.somewhere.unimportant.rest.resource.external.RegistrationResource class that supports GET. Verify that a method exists.
00000203 RequestProces I org.apache.wink.server.internal.RequestProcessor logException The following error occurred during the invocation of the handlers chain: WebApplicationException (405) with message 'null' while processing GET request sent to https://test.somewhere.com:88888/app777/public/events
Now what's even more interesting about this is that I receive this error locally, but I've also pushed my code up to a test environment where there are two load-balanced servers. In the test environment I receive this error on one of the servers consistently but the other one works consistently. So a 50% failure rate split on one successful server and one unsuccessful server.
Strange...why is it looking in that class for the endpoint I'm hitting?
-----------UPDATE #3-----------
Ok, I've taken out the use of the #ApplicationPath annotation in my PublicApplication class completely (as suggested from the comments below) and replaced it with a mere "/" but still receive a 404 error in the following scenario (note that for this test I have completely removed the "admin" services):
/* Public (and only) Service Set */
#ApplicationPath("/")
public class PublicApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> resources = new HashSet<Class<?>>();
resources.add(IncidentResource.class);
resources.add(IncidentDetailsResource.class);
resources.add(TicketResource.class);
resources.add(RegistrationResource.class);
return resources;
}
}
#Path("/public")
public class IncidentResource {
#GET
#Path("/incidents")
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidents() throws Exception {
//...
}
#Path("/public")
public class IncidentDetailsResource {
#GET
#Path("/v1/incidents/{incidentId}/details")
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidentDetails(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
#GET
#Path("/v2/incidents/{incidentId}/details")
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidentDetailsAsJson(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
}
#Path("/public/incidents/{incidentId}/tickets")
public class TicketResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getTickets(#PathParam("incidentId") String incidentId) throws Exception {
//...
}
}
#Path("/public/incidents/{incidentId}/registration")
public class RegistrationResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response register(#PathParam("incidentId") String incidentId, BaseRequestMessage<IncidentRegistrationRequest> baseRequestMessage) throws Exception {
//...
}
}
In this scenario I am getting this error:
org.apache.wink.server.internal.RequestProcessor logException The following error occurred during the invocation of the handlers chain: WebApplicationException (404 - Not Found) with message 'null' while processing GET request sent to http://test.somewhere.com:88888/app777/public/events
when trying to call:
GET /public/incidents
It may also be relevant that the other 4 endpoints above work just fine. And, more importantly, if I change the IncidentResource class to look like the following (moving the #Path annotation at the method level completely to the class level) then all 5 endpoints work:
#Path("/public/incidents")
public class IncidentResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getIncidents() throws Exception {
//...
}
I appreciate everyone who has stayed with me this far!
Remove #ApplicationPath("/context") or alternatively don't use the /context in the request uri or just use #ApplicationPath("/") it wont break even when you upgrade.
All the above different ways should work, but for your version I would recommend #ApplicationPath("/") and update your request uris by removing /context and everything should work the way you originally intended.
Apache Wink doesn't have support for ApplicationPath just yet.
https://issues.apache.org/jira/browse/WINK-398
This might work for you:
#Path("/incidents/{incidentId}/registration")
public class RegistrationResource1 {
#GET
public Response getRegisteredIncidents(#PathParam("incidentId") String incidentId) {
...
}
}
#Path("/incidents/registration/download")
public class RegistrationResource {
#GET
public Response downloadRegistrationIncidentsReport() {
...
}
}
However, I would not recommend splitting up the controller like that. Instead, just make a point of removing as much of the code inside each function to separate business logic classes.

405 method not allowed in spring mvc but other actions and controller working fine?

I am new to Spring and my other controller running good but when I am trying to call getmyfriends endpoint, I got the 405 Method Not Allowed:
#Controller
#Path("friends")
public class FreindsJersey {
#Autowired
private FriendsService friendsService;
#POST
#Path("getmyfriends")
#Produces(MediaType.APPLICATION_JSON)
public Response getAllMyFriends(String json) {
ReturnData returnData = (ReturnData) Parser.getJsonFromString(json, ReturnData.class);
return Response.ok(friendsService.getMyFriendsList(returnData).getContainer()).build();
}
#GET
#Path("unfriend/{userId}/{friendId}")
#Produces(MediaType.APPLICATION_JSON)
public Response unfriendUser(#PathParam("userId") long userId, #PathParam("friendId") long friendId) {
return Response.ok(friendsService.deleteAFriendOfTheUser(userId, friendId).getContainer()).build();
}
}
The URL I'm calling is http://localhost:8080/Indulgge/friends/getmyfriends
TL;DR: getAllMyFriends requires POST
When you enter a URL into your browser, it will use GET. You cannot POST from the URL bar.
Your code only allows POST.
#POST // <-- here
#Path("getmyfriends")
#Produces(MediaType.APPLICATION_JSON)
public Response getAllMyFriends(String json) {
ReturnData returnData = (ReturnData) Parser.getJsonFromString(json, ReturnData.class);
return Response.ok(friendsService.getMyFriendsList(returnData).getContainer()).build();
}
In fact, you have it backwards - safe and idempotent requests should be GET (such as getAllMyFriends); unsafe and non-idempotent requests should be POST (such as unfriendUser).

What's Jersey's default exception handling? (if an ExceptionMapper is NOT provided)

What's Jersey's default exception handling (when ExceptionMapper is NOT provided)?
Example:
#GET
#Path("/rest")
public String rest() {
throw new RuntimeException("Wonder what would happen...");
}
What would the result be? What would return in the HTTP status and content?
Your function have to return a Response object (javax.ws.rs.core.Response).
#GET
#Path("/rest")
public Response invokeSomething() {
return Response.ok("Some string").build();
}
The method ok return the 202 resoponse with Object serialised in annotation class. Es:
#Consumes(MediaType.APPLICATION_JSON)
#Produces({MediaType.APPLICATION_JSON})
public class ....
This annotation mean that my service consume a json and my response is a json.
Then, when you want return an error you have to return a code 500.
In your exemple:
#GET
#Path("/rest")
public Response invokeSomething() {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity("exeption message").build();
}

Categories