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
Related
I'm building a simple API framework on top of Weld CDI and Undertow, to get familiar with the CDI Portable Extension programming model. It's a strict subset of JAX-RS:
#Default
#Path("/dogs")
public class Dogs {
#Inject
private MyService service;
#GET
public Response get(#HeaderParam("DogDebug") String debugParam, #Inject DebugService debugger) { return BLAH; }
#Path("/{id}")
#GET
public Response getById(#PathParam("id") String param) { return BLAH; }
}
My CDI Portable Extension collects up all the AnnotatedTypes that have the Path annotation. When CDI finishes booting, an Undertow webserver starts and all the collected types (and their paths) are registered with an Undertow RoutingHandler.
The Extension is responsible for building HttpHandlers for each method that's annotated with #GET/#POST etc...
public HttpHandler getHandler(AnnotatedMethod<?> producer) {
Object contextualHandler = manager.createInstance()
.select(producer.getDeclaringType().getJavaClass()).get();
Preconditions.checkNotNull(contextualHandler, "Could not obtain a contextual reference to a handler for this");
Object result = producer.getJavaMember().invoke(contextualHandler);
Response response;
if(!(result instanceof Response)) {
response = Response.ok(result).build();
} else {
response = (Response) result;
}
response.write(exchange);
}
As you can see, right now the handler is using plain-ol Java Reflection to call the resource method.
I'd like to make method parameter injection work, as shown in my example above. I can use the BeanManager and metadata to grab the right parameters when actually running the handler, but ...
How can I validate the injection point? i.e. with an AnnotatedType I got from a ProcessAnnotatedType event, how can I validate an arbitrary method as if it were a producer or Constructor or event observer?
Update: So far, I've gotten pretty far with the InjectableMethod class from Deltaspike. It inspects the method and creates an InjectionPoint that can be passed to BeanManager.validate. However, it doesn't have much usage in the publicly Googleable code of the world.
If I'm understanding you correctly, then BeanManager#createInjectionPoint(AnnotatedParameter) is what you're looking for in terms of creating and validating the injection points. No need for DeltaSpike or other implementation specifics, it's already part of the spec.
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.
I have a resource, which is secured, if I remove the authentication, it appears to work, but then without the security, then what is the point?
Here is my code :
#POST
#Path("/secured")
#Timed
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#UnitOfWork
#RolesAllowed("mainUser")
public Response aliveSecure(#Auth User user, CustomRequest test)
{
CustomResponse resp = new CustomResponse();
System.out.println(test.getMessage());
return Response.status(Response.Status.ACCEPTED).entity(resp).build();
}
The CustomRequest and CustomResponse types are pretty standard POJOs, they just hold a string called "Message" - they are actually identical, but this is just an exercise I am trying to complete for the sake of learning DropWizard.
Now, if I remove the #Auth stuff here, and the #RolesAllowed, etc - making it a insecure, then the method performs as normal - but as is, this is the error I get when trying to start the application.
org.glassfish.jersey.server.model.ModelValidationException: Validation of the application resource model has failed during application initialization.
! [[FATAL] No injection source found for a parameter of type public CustomRequest at index 0.;
The auth manual reads it clear -
If you want to use #Auth to inject a custom principal type into your
resource.
Hence you shall ensure adding the following to your Service that extends io.dropwizard.Application
#Override
public void run(SomeConfigThatExtendsConfiguration config, Environment environment) throws Exception {
....
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
}
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!
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.