Nested inner class in Jersey / JAX-RS - java

I have this resource which is located perfectly:
#Path("/adoptable")
public class AdoptableAnimalsResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String get()
{
return "dogs";
}
}
Now, how can I turn this class into a nested inner class?
For example,
public class Grouper
{
#Path("/adoptable")
public class AdoptableAnimalsResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String get()
{
return "dogs";
}
}
}
When I try it, I get a 404 Not Found error, suggesting that Jersey is not considering the inner class as a resource.

You need to use Sub-Resource Locators. Basically, you will have a method in Grouper class, which will instantiate the AdoptableAnimalsResource class. The AdoptableAnimalsResource should not have a #Path annotation. It could but it will be ignored. It's methods can have sub-resource #Paths. The method in the Grouper class should have #Path that identities the AdoptableAnimalsResource sub-resource.
So it might look something like
#Path("/groups")
public class Grouper {
#Path("/adoptable")
public AdoptableAnimalsResource animalSubResource() {
return new AdoptableAnimalsResource();
}
public class AdoptableAnimalsResource {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String get() {
return "dogs";
}
}
}

Related

How to specifiy custom Request handling into spring MVC controller?

I have defined an class for some specific handling:
public abstract class ListProvider {
...
public abstract ResponseObject getResponse(RequestObject request) {}
}
I will create several derived class that I will define as services.
The purpose is to use it to create a json API using Jackson to deserialize the RequestObject and to serialize the ResponseObject. For example:
#Service
public class ClientListProvider extends ListProvider {
public ResponseObject getResponse(RequestObject request) {
return ...
}
I can use it in a controller like that:
#RestController
#RequestMapping("/client")
public class ClientController {
#AutoWired
ClientListProvider provider;
#PostMapping("/list")
public ResponseObject ResponseObject list(#RequestBody RequestObject request) {
return provider.getResponse(request);
}
}
But I would like to use it without the boiler plate, like:
#RestController
#RequestMapping("/client")
public class ClientController {
#PostMapping("/list")
#Provider(ClientListProvider.class)
public list() {}
}
Or maybe:
#RestController
#RequestMapping("/client")
#Provider(ClientListProvider.class,path="/list")
public class ClientController {
}
Or something like that.
Do you know if there any way? If necessary I could replace the Request/ResponseObject by HttpServletRequest/Response or something else in the ListProvider interface.

RESTful: How to dynamically extend path via interface

I have the following interfaces
#Path("/v1")
public interface IV1Api {
}
#Path("/Accounts/{AccountId}")
public interface IAccountsInstanceApi extends IV1Api {
}
#Path("/Users")
public interface IUsersListApi extends IAccountsInstanceApi {
#GET
Json listUsers();
#POST
Json createUser();
}
public UsersListResource implements IUsersListApi {
// ...
}
I was expecting my user list resource path to be /v1/Accounts/123/Users, but it is /Users. What am I doing wrong?
Sorry, but it doesn't work like this. You can do the following:
#Path(IAccountsInstanceApi.PATH)
public interface IAccountsInstanceApi extends IV1Api {
String PATH = "/Accounts/{AccountId}";
}
#Path(IUsersListApi.PATH)
public interface IUsersListApi extends IAccountsInstanceApi {
String PATH = IAccountsInstanceApi.PATH + "/Users";
#GET
Json listUsers();
#POST
Json createUser();
}

Dependcy injection in abstract controller failed

I have a base abstract controller class that contains a generic functionality. I also have a set of subclasses.
The abstract class has a property that i would like to Dependency inject. This property is common to all subclasses therefore i don't want it to be set on all the subclasses. but when I call abstract controller's function in subclass ,it turns out to be the property in abstract controller is null. I want to know why and how to fix it.Below is the code snippet:
Abstract Controller:
#Controller
public abstract class WebAPIBaseController {
#Resource
private IPermissionService permissionService;
public void validPermission(int user,String code){
permissionService.valid(user,code);
}
}
SubController
#Controller
#RequestMapping("/order")
public class OrderController extends WebAPIBaseController {
public String XXX(){
validPermission(1,"code");//it will throw a NullPointerException
}
}
besides,if I remove abstract controller(like below example) , it works good.
Remove abstract controller
#Controller
#RequestMapping("/order")
public class OrderController{
#Resource
private IPermissionService permissionService;
public void validPermission(int user,String code){
permissionService.valid(user,code);
}
public String XXX(){
validPermission(1,"code");//it works good
}
}
I don't think you need to inject the permissionService in the subclass, doing this you are hiding that of the superclass.
Have a look at this thread Spring can you autowire inside an abstract class? . You'll also find two other threads in one of the replies about this topic.
You could use #Autowired over the subclass constructor:
public abstract class WebAPIBaseController {
private final IPermissionService permissionService;
public WebAPIBaseController(IPermissionService permissionService) {
this.permissionService = permissionService;
}
public void validPermission(int user, String code){
permissionService.valid(user,code);
}
}
#Controller
#RequestMapping("/order")
public class OrderController extends WebAPIBaseController {
#Autowired
public OrderController(IPermissionService permissionService) {
super(permissionService);
}
public String XXX(){
validPermission(1,"code");//it will throw a NullPointerException
}
}

Passing variable from #Controller to #ControllerAdvice in SpringMVC

I created #ControllerAdvice that has to set me some model attribute.
#ModelAttribute
public void globalAttributes(Model model) {
model.addAttribute("pageId", PAGE_ID);
}
This is a generic example of what I need, and PAGE_ID represents some variable that actual controller has to set. Since #ControllerAdvice is running before controller, how can I declare this variable and use it in Advice? If that's even possible.
I think a better solution would been using some kind of abstract-class-pattern for your controller
public abstract class AbstractController {
#ModelAttribute
public void globalAttributes(Model model) {
model.addAttribute("pageId", getPageId());
}
abstract String getPageId();
}
public class MyController extends AbstractController {
#Override
public String getPageId() {
return "MyPageID"
}
//..your controller methods
}

Jersey/Jax-RS: how to filter resource and sub-resources

In Jersey 2, how can I bind a filter to all the method of a Resource as well as to all the methods of its sub-resources?
For example, if I have the following 2 resources:
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.server.model.Resource;
#Path("/myresource/{id: \\d+}")
#Produces(MediaType.APPLICATION_JSON)
#Singleton
class RootResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response get(#PathParam("id") Long id) {
return Response.ok().build();
}
#Path("/sub")
public Resource getSubResource() {
return Resource.from(SubResource.class);
}
}
#Produces(MediaType.APPLICATION_JSON)
#Singleton
class SubResource {
#GET
#Path("/{subid: \\d+}")
public Response get(#PathParam("id") Long id, #PathParam("subid") Long subid) {
return Response.ok().build();
}
}
I would like to filter RootResource.get(Long) and SubResource.get(Long, Long). But if I have other resources, those should not be filtered.
Using the DynamicFeature, we only have info on the Class and Method.
import javax.ws.rs.container.DynamicFeature;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.FeatureContext;
public class MyFeature implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
// Here how can I find out that SubResource is actually a sub-resource of RootResource
}
}
The idea is that I want to be able to filter out all calls for a certain set of id's (the set of id's is dynamic), with something more or less like this:
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
public class MyFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
for(Object resource:requestContext.getUriInfo().getMatchedResources()) {
if(resource instanceof RootResource) {
Long id = Long.valueOf(requestContext.getUriInfo().getPathParameters().getFirst("id"));
// ...
}
}
}
}
but I would like to avoid having to search for the matched resources. Is this possible?
I'm not 100% sure I understand the problem, but it seems that you want to want to limit which resources should go through the filter. For that you can simply use Name Binding.
Basic Steps:
Create a #NameBinding annotation
#NameBinding
#Target({METHOD, TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Filtered {
}
Annotate the filter
#Filtered
#Provider
public class MyFilter implements ContainerRequestFilter {
Annotate whatever root resources, resource methods, sub resource classes you want to be filtered
UPDATE
OK so after some playing around, I came up with a couple solutions.. netiher of which are pretty, but it gets the job done.
Keep in mind that configure in the DynamicFeature is called for each resource(method) we have.
Algorithm 1:
Get the method being checked and get its declaring class (in the case of a method in a sub resource, the declaring class will be the sub resource class)
Class<?> possibleSubResource =
resourceInfo.getResourceMethod().getDeclaringClass();
Build a temporary Resource from your root resource
Resource resource = Resource.from(SomeResource.class);
Iterate its child resources, checking if it's a resource locator
for (Resource childResource : resource.getChildResources()) {
if (childResource.getResourceLocator() != null) {
If is is resource locator get the return type.
ResourceMethod sub = childResource.getResourceLocator();
Class responseClass = sub.getInvocable().getRawResponseType();
Then check if the response type from step 4 == the declaring class from step 1.
if (responseClass == possibleSubResource) {
context.register(SomeFilter.class);
}
For the above to work, you actually need to return the sub resource type from the locator method, instead of a Resource. (You can try and make it work with Resource, but I haven't been able to figure it out)
#Path("{id}")
public SomeSubResource getSubResource() {
return new SomeSubResource();
}
Here is the full code that works (not battle tested :-)
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
Class<?> resourceClass = resourceInfo.getResourceClass();
if (resourceClass == SomeResource.class) {
context.register(SomeFilter.class);
}
Class<?> possibleSubResource = resourceInfo.getResourceMethod().getDeclaringClass();
Resource resource = Resource.from(SomeResource.class);
for (Resource childResource : resource.getChildResources()) {
if (childResource.getResourceLocator() != null) {
ResourceMethod sub = childResource.getResourceLocator();
Class responseClass = sub.getInvocable().getRawResponseType();
if (responseClass == possibleSubResource) {
context.register(SomeFilter.class);
}
}
}
}
Algorithm 2:
For this to work, we are going based off the assumption that what defines a Sub Resource is that it is annotation with #Path and has no Http method annotation
Get the method being checked and get its declaring class (in the case of a method in a sub resource, the declaring class will be the sub resource class)
Class<?> possibleSubResource =
resourceInfo.getResourceMethod().getDeclaringClass();
Iterate through the Methods in the root resource class
for (Method method : SomeResource.class.getDeclaredMethods()) {
Check if the method has an Http method annotation
boolean isHttpPresent = false;
for (Class annot : Arrays.asList(GET.class, POST.class, PUT.class, DELETE.class)) {
if (method.isAnnotationPresent(annot)) {
isHttpPresent = true;
break;
}
}
Check if the method has the #Path annotation. If it does, and it has not Http method annotations, then we register the filter
if (method.isAnnotationPresent(Path.class) && !isHttpPresent) {
Class subResourceClass = method.getReturnType();
if (subResourceClass == possibleSubResource) {
context.register(SomeFilter.class);
}
}
Here is the full code
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
Class<?> resourceClass = resourceInfo.getResourceClass();
if (resourceClass == SomeResource.class) {
context.register(SomeFilter.class);
}
Class<?> possibleSubResource = resourceInfo.getResourceMethod().getDeclaringClass();
for (Method method : SomeResource.class.getDeclaredMethods()) {
boolean isHttpPresent = false;
for(Class annot : Arrays.asList(GET.class,POST.class,PUT.class, DELETE.class)){
if (method.isAnnotationPresent(annot)) {
isHttpPresent = true;
break;
}
}
if(method.isAnnotationPresent(Path.class) && !isHttpPresent){
Class subResourceClass = method.getReturnType();
if (subResourceClass == possibleSubResource) {
context.register(SomeFilter.class);
}
}
}
}
Again, neither of these solutions are battled tested, but work for the few cases I have tried. Personally, I'd just go with the name binding, but maybe this is an issue you can raise with the Jersey team. This (automatic registration of sub resource, when the root resources are registered) does seem like something that should work out the box, or at least be able to be configured.
I had a similar need:
I wanted an annotation to specifically filter resource methods in order to achieve
something like this:
#Path("/api/sample")
#Produces(MediaType.APPLICATION_JSON)
public class SampleResource {
#Path("/filtered")
#GET
#Sample(value = "a sample value")
public Hello filtered() {
return new Hello("filtered hello");
}
#Path("/nonfiltered")
#GET
public Hello raw() {
return new Hello("raw hello");
}
}
My annotation being:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Sample {
String value() default "";
}
I ended up using a DynamicFeature to register a Filter on Resource
#Provider
public class SampleFeature implements DynamicFeature {
private SampleFilter sampleFilter;
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
if (resourceInfo.getResourceMethod().getAnnotation(Sample.class) != null) {
if (sampleFilter == null) {
this.sampleFilter = new SampleFilter();
}
context.register(sampleFilter);
}
}
}
the tricky thing was to find out how I could fetch the annotation value within my filter, hence to find out about ExtendedUriInfo, see below:
public class SampleFilter implements ContainerRequestFilter {
public SampleFilter() {
}
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
String sampleValue = this.getAnnotation(containerRequestContext).value();
// do some filtering based on the Sample Value
}
private Sample getAnnotation(ContainerRequestContext requestContext) {
ResourceMethod method = ((ExtendedUriInfo) (requestContext.getUriInfo()))
.getMatchedResourceMethod();
Method invokedMethod = method.getInvocable().getHandlingMethod();
return invokedMethod.getAnnotation(Sample.class);
}
}

Categories

Resources