I wrote a jax-rs service using jersey, and the class is as follows,
I want to define the url for the following like this,
One to get the reports by passing parameters
http://localhost:8085/DiginReport/rs/Reports/getreport/{test1:value,test2:value}
One to start the Engine:
http://localhost:8085/DiginReport/rs/Reports/start
#Path("Reports")
public class ReportService {
#GET
#Path("/getreport}/{parameters}")
#Produces(MediaType.TEXT_PLAIN)
public Response getReport(#PathParam("reportName") String reportName,#PathParam("parameters") String parameters) throws KettleXMLException, KettleMissingPluginsException, JSONException, UnknownParamException {
JSONArray jsonarr;
return Response.status(200).entity("ok").build();
}
#GET
#Path("{start}")
#Produces(MediaType.TEXT_PLAIN)
public Response startEngine(#PathParam("start") String command) {
return Response.status(200).entity("ok").build();
}
}
When i type this in url , it says resource not avaliable,
http://localhost:8085/DiginReport/rs/Reports/start
Try the following code.
I would also consider the following things:
Pass Json data as application/json instead of string as path parameter (note that you need to escape.
Use another method than GET to start the engine (e.g. POST), since get should be only used to retrieve data.
Use resources within the URL instead of start or getreports.
#Path("Reports")
public class ReportService {
#GET
#Path("/getreport/{parameters}")
#Produces(MediaType.TEXT_PLAIN)
public Response getReport(#PathParam("parameters") String parameters) throws KettleXMLException, KettleMissingPluginsException, JSONException, UnknownParamException {
JSONArray jsonarr;
return Response.status(200).entity("ok").build();
}
#GET
#Path("/start")
#Produces(MediaType.TEXT_PLAIN)
public Response startEngine() {
return Response.status(200).entity("ok").build();
}
}
Related
I am trying to provide endpoints that will listen on multiple versions, i.e /v1/test and /v2/test. In order not to duplicate my code, I use jersey's ability to use patterns in the #Path annotation.
Let's assume I want to provide a GET and a POST endpoint:
#Controller
#Slf4j
#Path("/")
public class TestController {
#GET
#Path("/v{version:[12]}/test")
#Produces(MediaType.APPLICATION_JSON)
public String test1(#PathParam("version") String version) {
System.out.println(String.format("GET /v%s/test called", version));
return "{\"foo\":\"bar\"}";
}
#POST
#Path("/v{version:[12]}/test")
#Produces(MediaType.APPLICATION_JSON)
public String test2(#PathParam("version") String version) {
System.out.println(String.format("POST /v%s/test called", version));
return "{\"foo\":\"bar\"}";
}
}
That works fine.
If I, however, try to use a specific path for the GET endpoints and use a pattern for the POST endpoint, I run into trouble.
Here the controller that would not work:
#Controller
#Slf4j
#Path("/")
public class TestController {
#GET
#Path("/v1/test")
#Produces(MediaType.APPLICATION_JSON)
public String test1() {
System.out.println("GET /v1/test called");
return "{\"foo\":\"bar1\"}";
}
#GET
#Path("/v2/test")
#Produces(MediaType.APPLICATION_JSON)
public String test2() {
System.out.println("GET /v2/test called");
return "{\"foo\":\"bar2\"}";
}
#POST
#Path("/v{version:[12]}/test")
#Produces(MediaType.APPLICATION_JSON)
public String test3(#PathParam("version") String version) {
System.out.println(String.format("POST /v%s/test called", version));
return "{\"foo\":\"barPOST\"}";
}
}
Doing GET /v1/test or GET /v2/test works fine, POST /v1/test however does not.
I get a 405 Method Not Allowed Exception.
As far as I got it the exception is thrown in the MethodSelectingRouter when it recognizes the path, but cannot find a method with the appropriate HTTP verb.
The issue seems to be that it picks the most specific path (/v1/test in my case) for which it does not know the POST verb.
Does anybody have an idea how to avoid this problem?
Cheers
PS: I am using spring boot with jersey (i.e. spring-boot-starter-web and spring-boot-starter-jersey) in version 1.5.2.RELEASE
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.
I have currently trying to learn the basics of Java REST, using JAX-RS.
Within the UserService class (near bottom) of this example there is both an #GET AND #PUT method with the same #path annotation:
#GET
#Path("/users")
#Produces(MediaType.APPLICATION_XML)
public List<User> getUsers() {
return userDao.getAllUsers();
}
and
#PUT
#Path("/users")
#Produces(MediaType.APPLICATION_XML)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String createUser(#FormParam("id") int id,
#FormParam("name") String name,
#FormParam("profession") String profession,
#Context HttpServletResponse servletResponse) throws IOException {
User user = new User(id, name, profession);
int result = userDao.addUser(user);
if(result == 1) {
return SUCCESS_RESULT;
}
return FAILURE_RESULT;
}
How does the Program know which method to invoke, considering that they are both point at the same #path ?
Resource classes have methods that are invoked when specific HTTP method requests are made, referred to as resource methods. In order to create Java methods that will be invoked with specific HTTP methods, a regular Java method must be implemented and annotated with one of the JAX-RS #HttpMethod annotated annotations (namely, #GET, #POST, #PUT, and #DELETE).
For more info take a look at this example1 and example2
JAX-RS evaluates the HTTP method of the request and then calls the appropriate Java method in UserService.
Consider this case:-
I am injecting HttpServletRequest in a Rest Service like
#Context
HttpServletRequest request;
And use it in a method like:-
#GET
#Path("/simple")
public Response handleSimple() {
System.out.println(request.getParameter("myname"));
return Response.status(200).entity("hello.....").build();
}
This works fine but when I try to send it through POST method and replace the #GET by #POST annotation, I get the parameter value null.
Please suggest me where I am mistaking.
You do not need to get your parameters etc out of the request. The JAX-RS impl. handles that for you.
You have to use the parameter annotations to map your parameters to method parameters. Casting converting etc. is done automaticly.
Here your method using three differnt ways to map your parameter:
// As Pathparameter
#POST
#Path("/simple/{myname}")
public Response handleSimple(#PathParam("myname") String myName) {
System.out.println(myName);
return Response.status(200).entity("hello.....").build();
}
// As query parameter
#POST
#Path("/simple")
public Response handleSimple(#QueryParam("myname") String myName) {
System.out.println(myName);
return Response.status(200).entity("hello.....").build();
}
// As form parameter
#POST
#Path("/simple")
public Response handleSimple(#FormParam("myname") String myName) {
System.out.println(myName);
return Response.status(200).entity("hello.....").build();
}
Documentation about JAX-RS Annotations from Jersey you can find here:
https://jersey.java.net/documentation/latest/jaxrs-resources.html
I'm developing a rest web service using jax rs. In the current method i want to hava a #GET request where the user is passing a json parameter named "request" using the header of the http request.
Code:
#GET
#Path("load")
#Produces(MediaType.APPLICATION_JSON)
public LoadUserResponse load(#HeaderParam("request") LoadUserRequest sRequest) throws Exception{
User user= userBean.load(sRequest.getId());
LoadUserResponse response = new LoadUserResponse();
response.Transform(user);
return response;
}
this code gives me the exception :
java.lang.RuntimeException: Unable to find a constructor that takes a String param or a valueOf() or fromString() method for javax.ws.rs.HeaderParam(\"request\") on public com.totempool.rest.responses.LoadUserResponse com.totempool.rest.services.UserService.load(com.totempool.rest.requests.LoadUserRequest) throws java.lang.Exception for basetype: com.totempool.rest.requests.LoadUserRequest"}}
POJO class:
package com.totempool.rest.requests;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class LoadUserRequest {
#XmlElement(name="id")
private long id;
public LoadUserRequest(){
}
public long getId() {
return id;
}
public LoadUserRequest fromString(String param){
return null;
}
}
My question is , is there a way to pass a #HeaderParam and autoparse it to the object?
By autoparse i mean something like this:
#POST
#Path("list")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public ListUserResponse list(ListUserRequest request) throws Exception{
List<User> users= userBean.list();
ListUserResponse response = new ListUserResponse();
response.Transform(users);
return response;
}
with this code the app will recognize the json send and parse it into an object.
A POSSIBLE OPTION BUT NOT SUITABLE FOR WHAT I NEED:
#GET
#Path("load")
#Produces(MediaType.APPLICATION_JSON)
public LoadUserResponse load(#HeaderParam("request") String sRequest) throws Exception{
ObjectMapper map= new ObjectMapper();
LoadUserRequest request=map.readValue(sRequest, LoadUserRequest.class);
User user= userBean.load(request.getId());
LoadUserResponse response = new LoadUserResponse();
response.Transform(user);
return response;
}
here i'm getting the string send and then parsing it manually, the problem is that the web service may have several similar methods and i don't want to do this manually in every one of them.
In my case it works with the following solution (GSON from Google). I don't know if this is the best one, but it works :-) I put this code in every REST-Transfer-Class.
/**
* Parse JSON to Object.
*
* #param p_json JSON String
* #return Object
*/
public static POJO_XY valueOf(String p_json) {
return gson.fromJson(p_json, POJO_XY.class);
}