0

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!

risingTide
  • 1,754
  • 7
  • 31
  • 60
  • Good idea. Originally I had **not** had the slashes and added them in the hopes that they would help. I did, however, just remove them again at your suggestion but it did not make a difference. – risingTide Sep 27 '16 at 15:23
  • @Roman Vottner: In an extension of the JAX-RS [Application](http://docs.oracle.com/javaee/6/api/javax/ws/rs/core/Application.html) class; I've edited my question to include that above. – risingTide Sep 27 '16 at 16:39
  • I would have expected that to work too. I definitely have a WebSphere service with two different `@GET` methods on different paths, although neither of my methods have no parameters at all. Have you tried with some different path/param combinations, just to try to isolate a specific cause? Also, what version of WebSphere? And have you looked at the WebSphere logs for error messages on startup and on request received? – dbreaux Sep 28 '16 at 15:58
  • 1
    I have multiple combinations of methods with other path/param combinations and those are fine. There are no WebSphere errors on startup and the exact version of WebSphere is 8.5.5.5. I've modified the original question with the actual error message I receive as well. – risingTide Oct 03 '16 at 15:29
  • I'm just grasping at things to try, to isolate root cause here... I have you tried removing the /id/registration one, and seeing if the other two by themselves behave the same way? And also try removing just one of those two no-param ones and seeing what happens? My gut says maybe it has to do with the no-param methods, but I have no concrete reason for that hunch. – dbreaux Oct 03 '16 at 15:45
  • I definitely appreciate the brainstorming dbreaux! I think the solution has been found however. Please see below. – risingTide Oct 03 '16 at 15:56

2 Answers2

2

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

s7vr
  • 73,656
  • 11
  • 106
  • 127
  • 1
    I wasn't expecting this to be the root cause but it apparently is. If I replace `@ApplicationPath("/context")` with `@ApplicationPath("/")` then all the services work. The issue might be that I have two separate classes extending `javax.ws.rs.Application` - essentially a "/contextOne" and "/contextTwo". Perhaps there were conflicts with both there and changing one to simply "/" eliminates the conflict. What's disturbing is that we have other applications running with multiple `Application` extensions and they work fine. It must have to do with certain url combos between the two? – risingTide Oct 03 '16 at 15:54
  • Hmm...now I'm thinking it's not related to the dual Application extension at all because if I switch which annotation has the "/" I still have the same problem. I must remove the actual "contextOne" that involves the resource above to make it work. So the issue must be directly with Wink and the combination in that resource described in the question above. – risingTide Oct 03 '16 at 16:40
  • I think working around application path annotation is your only option for now. – s7vr Oct 03 '16 at 17:12
  • Back to the drawing board on this. If I remove the @Application("/context") annotation and simply add the "/context" at the beginning of all of the @Path annotations in each resource class _I still have issues_. This must mean that there is some conflict between resource classes. Still investigating... – risingTide Oct 03 '16 at 17:25
  • adding "/context" to the @Path annotation worked fine for me but I dont have the same set up as you do. Add your findings if you want me to take a look. – s7vr Oct 04 '16 at 16:56
  • Sorry for the delay. I updated my post above and now have all of my endpoints listed for the entire application. Assumedly something is conflicting. I appreciate your time! – risingTide Oct 10 '16 at 15:12
  • I updated the post again with one of my latest 405 error messages that could be helpful. – risingTide Oct 11 '16 at 03:32
  • Sorry for the late reply. you really need to remove the /public & /admin from the application class. Merge both the admin and public application classes into one application class with no application path annotation and add the /public and /admin to the respective @Path annotation of resource classes. This should fix your conflicts. – s7vr Oct 15 '16 at 19:45
  • Now I'm sorry for *my* late reply! I completely removed the admin endpoint set entirely and removed the "public" from the application class just keeping the "/" in the annotation. However, I still am experiencing the same issue. I added the newly updated code in another update above with the specific 404 error. I appreciate your continued help! – risingTide Oct 28 '16 at 19:46
0

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.

Mike Thomsen
  • 36,828
  • 10
  • 60
  • 83
  • Thanks Mike. Yes, that will work actually; as I stated above I can get it to work by splitting it into different classes. But my question is trying to understand why the original implementation won't work. – risingTide Sep 27 '16 at 16:05