Apache-CXF JAX-RS implementation not respecting proper #Path ordering - java

I have a JAX-RS webservice that looks like this:
#Path("/service")
public interface Service {
#GET
#Path("/{serviceId}")
public Response getService(#PathParam("serviceId") String serviceId);
#GET
#Path("/{serviceId}/private")
public Response getPrivateService(#PathParam("serviceId") String serviceId);
#GET
#Path("/other-thing")
public Response getOtherThing(#CookieParam("cookieName") String cookieValue);
}
For some reason, GET /other-thing always invokes the first method with #Path("/{serviceId}"). Invoking GET /abc/private returns a 404 claiming there is no matching route. According to the spec, the path with the most matching literal characters should be selected, but it seems as if my routes are being completely ignored. How can I debug this?
Here is the log message from CXF:
No operation matching request path "/service/abc/private" is found, Relative Path: /abc/private, HTTP Method: GET, ContentType: */*, Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8,. Please enable FINE/TRACE log level for more details.

I discovered the problem.
I have recently switch from Eclipse to IntelliJ. Eclipse's default behavior is to ignore any annotations when auto-generating interface method implementations. IntelliJ, on the other hand, keeps the annotations. Here is the different:
// In Eclipse
public class ServiceImplementation implements Service {
public Response getService(String serviceId) {
return null;
}
}
// In IntelliJ
public class ServiceImplementation implements Service {
// Note the #PathParam
public Response getService(#PathParam("serviceId") String serviceId) {
return null;
}
}
The additional annotations in the implementation of the service causes path resolution to fail. Since I had implemented getService in Eclipse, it worked correctly, but the new methods implemented IntelliJ did not work until I removed the parameter annotation in the service implementation.

Related

Content-Type is missing with Spring GET/POST method

I am new to Spring and I am trying to do the basic GET and POST method.
This is how I am trying to do the methods:
#RestController
public class DeskController {
#Autowired
private DeskDao dao;
#GetMapping("desks")
public List<Desk> getDesks() {
System.out.println(dao.findById(1L));
return dao.findAll();
}
#PostMapping("desks")
public Desk save(#RequestBody #Valid Desk desk) {
Desk deskObj = dao.save(desk);
System.out.println(deskObj);
return deskObj;
}
When I am calling the POST method like this I get the pring with the actual object that I had called it with so it is working fine, but I also get this error:
javax.ws.rs.ProcessingException: Content-Type is missing
And when trying to call GET it tells me that:
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'GET' not supported
I am aware that I have not included the whole code, but I will add what is needed to resolve this error since there are a lot of classes.
My questions are, what do I do against the first error and why is GET method not supported?
Two things you need to change :
Use a / to indicate that for this path you will perform an
operation. For eg : (/desks)
Use the annotation #Consumes to
indicate that this method accepts the payload in particular format. For eg : #Consumes(MediaType.APPLICATION_JSON) annotated over your save() method.

How to intercept a request in Jersey using Annotations?

I'm using Jersey to build a REST API with your standard annotation based routing.
My question is, how can I use a "custom annotation" (in this case #InternalOnly) to intercept a request and run code before the request hits the resource and the // DO STUFF block is executed?
#Path("app")
#Produces(MediaType.APPLICATION_JSON)
public final class SomeResource {
#GET
#Path("something")
#InternalOnly
public Response getSomething() {
// DO STUFF
}
}
I would then have another class that would handle the #InternalOnly header...
public final class InternalOnlyHandler implements SomethingProbably {
public void handle(Object someContext) {
// HANDLE INTERNAL ONLY AUTHENTICATION
}
}
My purpose is basically I need certain API resources to be available internally ONLY and others publicly available and the Authentication is slightly different for both so registering a simple ContainerRequestFilter won't do the job.
I can't figure this out -- please help!

REST Subresource Locators

I am reading through the book RESTful Java with JAX-RS 2.0, 2nd Edition and am struggling to understand how Subresource Locators work, below is a cut-down version of the example provided.
CustomerDatabaseResource class
#Path("/customers")
public class CustomerDatabaseResource {
#Path("{database}-db")
public CustomerResource getDatabase(#PathParam("database") String db) {
// find the instance based on the db parameter
CustomerResource resource = locateCustomerResource(db);
return resource;
}
protected CustomerResource locateCustomerResource(String db) {
...
}
}
CustomerResource Class
public class CustomerResource {
private Map<Integer, Customer> customerDB =
new ConcurrentHashMap<Integer, Customer>();
private AtomicInteger idCounter = new AtomicInteger();
public CustomerResource(Map<Integer, Customer> customerDB)
{
this.customerDB = customerDB;
}
#GET
#Path("{id}")
#Produces("application/xml")
public StreamingOutput getCustomer(#PathParam("id") int id) {
...
}
So I understand that as a request such as GET /customers/northamerica-db/333 comes in, will first match the expression on the method CustomerDatabaseResource.getDatabase() which based upon the location, will create the correct instance of CustomerResource.
What I don't understand is what happens next...
The instance resource gets returned, but returned to where?
How does the web service know to then match and process the remaining part of the request with the method CustomerResource.getCustomer()? I guess this is because The CustomerDataBaseResource class doesn't have a #GET, but I don't really understand how the transition happens.
Is this specific to RESTEasy?
The instance resource gets returned, but returned to where?
It's get's returned to the request processing engine and continues to look for a matching method (inside the return resource object), just like any other request.
How does the web service know to then match and process the remaining part of the request with the method CustomerResource.getCustomer()? I guess this is because The CustomerDataBaseResource class doesn't have a #GET, but I don't really understand how the transition happens
Resource locators aren't supposed to be annotated with Http Methods. That's how they are known to be locators. Since it is not the resource method to be called, it should not be annotated. Imagine this
public class CustomerResource {
#PUT
#Path("{id}")
public Response updateCustomer(Customer customer) {}
#POST
#Path("{id}")
public Response createCustomer(Customer customer) {}
}
If CustomerDataBaseResource.getDatabase() were to be annotated with an Http method, then we couldn't hit the above methods. All the locator needs is the #Path, and the URI matching will continue starting from that path.
/customers/database-us
Once the CustomerResource is created, if the request uri is /customers/database-us/123, then now the next logical step is to find a matching resource method based on the URI, so will be looking for something annotated with #Path that will match 123. Then the Http method is checked.
Is this specific to RESTEasy?
Going through the jax-rs spec, I don't see anything about sub-resource locators, but Jersey also implements this exact behavior. I've read the book you are referring to, and from what I remember, the author doesn't really get much into anything that is implementation specific, but does mention common feautres that most implementers implemented, that is not part of the spec. Maybe this is one of those things.
UPDATE
So it is in the spec. Go to the link and download the spec. You will find everything under 3.4.1 Sub Resources and some algorithm info for request matching in 3.7.2 Request Matching

Use header in addition to method to route request to annotated method

I wonder if it's possible with JAX-RS to route a request using an header in addition to the HTTP method. In fact, I can't find out the way to do that.
I thought about something like that where x-header is an header:
#Path("/contacts/")
public class MyResource {
#POST
#Header("x-header:content1")
public void method1(MyContent1 content) {
(...)
}
#POST
#Header("x-header:content2")
public void method2(MyContent2 content) {
(...)
}
}
This question follows this answer: How to Update a REST Resource Collection.
Thanks very much for your help!
Thierry
If you need to affect the request matching/routing process, you have to use JAX-RS filters - PreMatching filters to be specific (#PreMatching) [this will work in JAX-RS 2.0 onwards] If the use header info in the resource methods, it wont make sense because JAX-RS would have already matched the method
Here is the overall flow in filter implementation
Use the ContainerRequestContext to fetch header info
Apply your business criteria depending on the header value
Now the trick is to be able to route to the desired resource method - one option you have is to use the setRequestUri method of ContainerRequestContext and have different resource methods set on different URIs (using #Path)
Jersey docs might help -- https://jersey.java.net/documentation/latest/filters-and-interceptors.html#d0e9538
Just throw in another option. You can also use Sub-resource locators, which give us some control over the chosen resource (and is part of the JAX-RS spec). For example
#Path("contacts")
public class ContactsResource {
#Path("/")
public AbstractHeaderResource doSomething(#HeaderParam("X-Header") String xHeader) {
if ("RequiredValue".equals(xHeader)) {
return new WithHeaderResource();
}
return new WithOutHeaderResource();
}
public static abstract class AbstractHeaderResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public abstract Response doSometing(Contact contact);
}
public static class WithHeaderResource extends AbstractHeaderResource {
#Override
public Response doSometing(Contact contact) {
return Response.ok("*** With Header ***").build();
}
}
public static class WithOutHeaderResource extends AbstractHeaderResource {
#Override
public Response doSometing(Contact contact) {
return Response.ok("*** WithOut Header ***").build();
}
}
}
Test
C:\>curl -v -X POST
-d "{\"name\":\"Peeskillet\"}"
-H "X-Header:RequiredValue"
-H "Content-Type:application/json"
http://localhost:8080/contacts
*** With Header ****
C:\>curl -v -X POST
-d "{\"name\":\"Peeskillet\"}"
-H "Content-Type:application/json"
http://localhost:8080/contacts
*** WithOut Header ****
The resource methods don't have to accept the same parameter type. I did it just for brevity. You can have the sub resource classes extend an empty abstract class or interface (just for typing), and create the methods however you want
UPDATE
If you need some objects injected into the sub resource, you can also return the sub resource class from the locator method or you can inject ResourceContext into the main resource class and use that to create the sub resource instance and return that instance from the locator method. Both ways will support container injection as the container will create the instance instead of you just instantiating it.

Annotating resource to produce JSON, but return "text/plain" in response header

I am currently implementing a web API
Spring
Jersey
com.thetransactioncompany.cors http://software.dzhuvinov.com/cors-filter.html
The output (if any) will be JSON, so all my classes are annotated with the expected media type.
#Produces(MediaType.APPLICATION_JSON)
public class CustomerResource {
...
}
that way my classes are automatically transformed to json.
BUT...
Due to microsoft, their IE only support CORS, if the request/response type is text/plain http://blogs.msdn.com/b/ieinternals/archive/2010/05/13/xdomainrequest-restrictions-limitations-and-workarounds.aspx
4. Only text/plain is supported for the request's Content-Type header
so I need to force my application to respond with text/plain in the header but still projecting my classes to json output. I know that the CORS classes I added is setting that header, but somehow that is overwritten again by my annotation, even if I add another filter by my own.
Hum, the link you are pointing says that it is true for REQUESTS only.
So you can accept only text plain but are free to produce what ever you want.
EDIT Try registering a custom responsefilter with code similar to that (maybe you already did it?):
#Provider
public class HeaderRewriteFilter implements ContainerResponseFilter {
#Override
public ContainerResponse filter(ContainerRequest request, ContainerResponse response) {
response.setResponse(Response
.fromResponse(response.getResponse()).header(HttpHeaders.CONTENT_TYPE, "text/plain").build());
return response;
}
}
However check the result to ensure it is ok if the response already contains this header.
Otherwise you can try to modify the current response, but I am not sure you can as it might be an immutable object. And by the way it looks less clean to me :)
List<Object> contentTypes = response.getHttpHeaders().get(HttpHeaders.CONTENT_TYPE);
contentTypes.clear();
contentTypes.add("text/plain");
Also for doing json<>java databiding you can check Genson library http://code.google.com/p/genson/, it integrates well with Jersey. Just drop the jar in the classpath and run!
EDIT 2 OK then you must do it in the other way, use produces "text/plain" and define a json bodywriter for for that type. Downside is that you will be able to produce only json. With Genson you could do it that way:
#Provider
#Produces({ MediaType.TEXT_PLAIN })
public class PlainTextJsonConverter extends GensonJsonConverter {
public GensonJsonConverter() {
super();
}
public GensonJsonConverter(#javax.ws.rs.core.Context Providers providers) {
super(providers);
}
}

Categories

Resources