How to reuse a REST methods in different resources? - java

Hi,
I am building a REST-api using Jersey and Java. I wonder if it is possible to reuse a method in many resources.
As an example If I have this code:
#Path("/users")
public class UserResource {
#GET
#Path("/{uid}/comments")
#Produces(MediaType.APPLICATION_JSON)
public List<Comment> getComments() {
return commentService.getOnEntity("User", uid);
}
}
and this:
#Path("/items")
public class ItemResource {
#GET
#Path("/{uid}/comments")
#Produces(MediaType.APPLICATION_JSON)
public List<Comment> getComments() {
return commentService.getOnEntity("Item", uid);
}
}
Is it possible to reuse the code for specifying the method "/{uid}/comments/" so I do not need to write it in every resource that is going to need it?
I guess I could extend a CommentResource with the method, but the I can only add one set of methods. If I use Interface I could specify more than one set of methods but would have to rewrite the code inside the methods in every resource.
Edit
After a tips from #thomas.mc.work I rewrote my code using a sub resource. It is better than the first solution since I get all methods from my sub resource and it only takes 4 lines of code per resource. This is how it looks like:
#Path("/users")
public class UserResource {
#Path("/{uid}/comments")
public CommentSubResource getCommentSubResource(#PathParam("uid") String uid) {
return new CommentSubResource("User", uid);
}
}
and this:
#Path("/items")
public class ItemResource {
#Path("/{uid}/comments")
public CommentSubResource getCommentSubResource(#PathParam("uid") String uid) {
return new CommentSubResource("Item", uid);
}
}
and this:
public class CommentSubResource {
private String entity;
private String entityUid;
public CommentSubResource(String entity, String entityUid) {
this.entity = entity;
this.entityUid = entityUid;
}
#GET
#Path("/")
#Produces(MediaType.APPLICATION_JSON)
public List<Comment> getComments() {
return commentService.getOnEntity(entity, entityUid);
}
#DELETE
#Path("/")
#Produces(MediaType.APPLICATION_JSON)
public List<Comment> deleteComment(#PathParam("uid") String uid) {
return commentService.delete(uid);
}
}
This is much better. I have an idea to use java 8 and default implementation interfaces to be able to just implmenet an interface to get the functionality, but I am not sure if I am able to determine which resource the default implemented method is called from.
Edit
After some laboration I think subresources is the way to go, even if it´s not (according to me) the perfect solution.

There is a similar feature called "Subresource Locators". You can decide in runtime which Resource is selected to process the request that is matching your JAX-RS method.

Related

How to make multiple #Path in a #GET in JAX-RS API?

So, in my Resource class I have the following:
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Carta> get() {
return repositorio.getAll();
}
#GET
#Path("{id}")
#Produces(MediaType.APPLICATION_JSON)
public Carta getById(#PathParam("id") int id) {
return repositorio.getID(id);
}
both works, one is a general get (will get all) and the other get by ID. I need to add a third get by String but I'm failing with the #params. I need to add this to the Resource class:
#GET
#Path("{nome}")
#Produces(MediaType.APPLICATION_JSON)
public List<Carta> getNome(#PathParam("nome") String nome) {
return repositorio.getString(nome);
}
if I comment the getById lines, my getByString works, the code is good, i just need to make both function at the same time, if it receives a number, it looks for an ID, if its a String it looks into name and description. Also, I wonder if it's better code practice to create more endpoints? Like /search/byid/xx and /search/byname/xxx instead of a general /search/xxx? Thanks.
Rather than creating a different endpoint, you should enhance List<Carta> get() to support filtering by card name.
The expected way to do that is to use the #QueryParam annotation like this:
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Carta> get(#QueryParam("name") String name) {
return repositorio.getCartas(name);
}
In the case of null name parameter, all Carta instances will be returned.

Quarkus & Microprofile : Is there a better way to use a property from application.properties into #ClientHeaderParam?

I'm trying to build a simple app that calls an API with quarkus-rest-client.
I have to inject an API Key as a header which is the same for all resources of the API.
So I would like to put the value of this API Key (that depends on the environment dev/qa/prod) in the application.properties file located in src/main/resources.
I tried different ways to achieve this:
Use directly com.acme.Configuration.getKey into #ClientHeaderParam value property
Create a StoresClientHeadersFactory class which implements ClientHeadersFactory interface to inject the configuration
Finally, I found the way described below to make it work.
My question is: Is there a better way to do it?
Here's my code:
StoreService.java which is my client to reach the API
#Path("/stores")
#RegisterRestClient
#ClientHeaderParam(name = "ApiKey", value = "{com.acme.Configuration.getStoresApiKey}")
public interface StoresService {
#GET
#Produces("application/json")
Stores getStores();
}
Configuration.java
#ApplicationScoped
public class Configuration {
#ConfigProperty(name = "apiKey.stores")
private String storesApiKey;
public String getKey() {
return storesApiKey;
}
public static String getStoresApiKey() {
return CDI.current().select(Configuration.class).get().getKey();
}
}
StoresController.java which is the REST controller
#Path("/stores")
public class StoresController {
#Inject
#RestClient
StoresService storesService;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Stores getStores() {
return storesService.getStores();
}
}
Late to the party, but putting this here for my own reference. There seems to be a difference in using #ClientHeaderParam and #HeaderParam, so I investigated a little further:
According to the Microprofile docs, you can put a compute method for the value in curly braces. This method can extract the property value.
See link for more examples.
EDIT: What I came up with resembles the original, but uses a default method on the interface, so you can at least discard the Configuration class. Also, using the org.eclipse.microprofile.config.Config and ConfigProvider classes to get the config value:
#RegisterRestClient
#ClientHeaderParam(name = "Authorization", value = "{getAuthorizationHeader}")
public interface StoresService {
default String getAuthorizationHeader(){
final Config config = ConfigProvider.getConfig();
return config.getValue("apiKey.stores", String.class);
}
#GET
#Produces("application/json")
Stores getStores();
I will get rid of the Configuration class and use an #HeaderParam to pass your configuration property from your rest endpoint to your rest client. The annotation will then send this property as an HTTP header to the remote service.
Somthing like this should works:
#Path("/stores")
#RegisterRestClient
public interface StoresService {
#GET
#Produces("application/json")
Stores getStores(#HeaderParam("ApiKey") storesApiKey);
}
#Path("/stores")
public class StoresController {
#ConfigProperty(name = "apiKey.stores")
private String storesApiKey;
#Inject
#RestClient
StoresService storesService;
#GET
#Produces(MediaType.APPLICATION_JSON)
public Stores getStores() {
return storesService.getStores(storesApiKey);
}
}

JAX-RS with RESTeasy Factory

I am trying to avoid boilerplate code in my client's requests handling through JAX-RS and RESTeasy. I have several classes, example
class User{
private String name;
private String username;
private String address;
private long id;
//getters and setters
}
class Company{
private String name;
private String address;
private String location;
//getters and setters
}
I want to have create(), update(), delete(), and getAll() responses. For example, For the User I would have the following class with a create method and subsequent update, delete and getAll:
#Path ("/user")
public class UserApi {
#PersistenceUnit
private EntityManagerFactory emf;
#POST
#Path("/create")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String Create(#Form User user){
EntityManager em = emf.createEntityManager();
try{
em.getTransaction().begin();
em.merge(user);
em.getTransaction().commit();
}
catch (Exception e){
e.printStackTrace();
em.getTransaction().rollback();
return "{\"success\":false, \"msg\":\"Error occured, please try later\"}";
}
return "{\"success\":true, \"msg\": \"Saved successfully\"}";
}
I would have to repeat the same code for each of the classes I have. Someone had commented on use of a factory class to avoid this, perhaps using generics or an Interface. I have had a hard time figuring it out. Please suggest a good design to solve this.
You must ask yourself what the differences between all implementations will be.
In this case, not much. You can copy/paste this piece of code, replace #Path("/user") with #Path("/company"), type User with Company and Bob's your uncle. Em.merge() takes any object, so we don't really care.
So because the #Path annotation is different for all, it would be acceptible to create a new Api like this;
#Path("/users")
#Stateless
public class UserApi extends AbstractCrudApi<User> {}
All elements that are unique to this REST service are defined. We have our #Path and User which is passed as a Generic Type.
Now we create this AbstractCrudApi abstract class. abstract, because we want to implement the common logic for all future services. Interfaces,as you already know, cannot have implemented methods. (Let's just forget about java 8 default methods)
#Produces(MediaType.APPLICATION_JSON)
public abstract class AbstractCrudApi<T> {
#PersistenceContext
private EntityManager em;
#POST
#Path("/create")
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public String create(#Form T entity) {
try {
em.getTransaction().begin();
em.merge(entity);
em.getTransaction().commit();
} catch (Exception e) {
e.printStackTrace();
em.getTransaction().rollback();
return "{\"success\":false, \"msg\":\"Error occured, please try later\"}";
}
return "{\"success\":true, \"msg\": \"Saved successfully\"}";
}
}
And that is all there is to it.
I don't quite see how a factory can be of any advantage here.
Another tip; try using the javax.ws.rs.core.Response return type instead of String. This will allow you to set a http response status as well as your response body. You can also try using Jackson to marshall your response objects. Check resteasy-jackson2-provider.

Why should an Interface be declared for JAX-RS implementation?

I have gone through couple of tutorials, where I could see that an Interface which JAX-RS annotation is created. And later an implementation of the same is done.
Why is so? Can't I expose a concrete class directly as a RESTful Service? Is that a bad practice? Below is one of the samples which I came across in this question.
public interface ICRUD {
#POST
#Consumes("application/json")
#Produces("application/json")
#Path("create")
public String createREST(String transferObject);
#GET
#Consumes("application/json")
#Produces("application/json")
#Path("retreive/{id}")
public String retreiveREST(#PathParam("id") String id);
#POST
#Consumes("application/json")
#Produces("application/json")
#Path("update")
public void updateREST(#Suspended final AsyncResponse asyncResponse,
final String transferObject) ;
#DELETE
#Consumes("application/json")
#Produces("application/json")
#Path("delete/{id}")
public String deleteREST(#PathParam("id") String id);
}
Can't I expose a concrete class directly as a RESTful Service?
You definitely can. Have you tried it? It should work just fine.
Is that a bad practice?
Personally (and this is just my preference), I think it's bad practice to use interfaces. Some people may argue that it cleans up your code, but there are problems that come with using interfaces, for instance annotation inheritance can sometimes cause a problem for those who don't understand what the problem is. It can be really hard to spot.
If your argument is that interfaces make code cleaner, I have a couple arguments.
(1) Your code is less understandable. You need to keep referring back to the interface to see what arguments are for (e.g. inspecting the method parameter annotations). It's easier when all the annotations are in the code your actually writing.
(2) Interfaces have no implementation, so you would still need to implement every class. I personally go with an abstract base class that will implement all the basic operations. For example
public abstract class AbstractResource<T extends BaseEntity> {
private final Repository<T> repository;
public AbstractResource(Repository<T> repository) {
this.repository = repository;
}
#GET
public List<T> getAll() {
return this.repository.findAll();
}
#GET
#Path("{id}")
public T getOne(#PathParam("id") long id) {
T result = this.repository.findOne(id);
if (result == null) {
throw new NotFoundException();
}
return result;
}
#POST
public Response create(T entity, #Context UriInfo uriInfo) {
T saved = this.repository.save(entity);
// BaseEntity should have an id property
long id = saved.getId();
URI createdUri = uriInfo.getAbsoluteUriBuilder()
.path(id).build();
return Response.created(createdUri).build();
}
}
You could do the same for #PUT and #DELET. The core functionality is the same for all resource collections. The only thing that would need to change is the Repository type. All your implementations could just extend it like
#Path("pets")
public class PetsResource extends AbstractResource<Pet> {
#Inject
public PetsResource(PetsRepository repository) {
super(repository);
}
}
This is much cleaner. You don't need to implement the same basic CRUD operations for your concrete resources. If you want to provide other resource methods in your concrete resource class, you can do so.
To say in short - the interface is not mandatory. You can expose a class as a service.
Here's a great discussion about this
https://softwareengineering.stackexchange.com/questions/150045/what-is-the-point-of-having-every-service-class-have-an-interface

How to get resource object in RequestFilter in Jersey

As I understand, it's possible to get resource class and method using injected ResourceInfo, but I need to execute some method of my resource class in request filter, so I need to get this object.
Or maybe the whole idea of getting exact object is wrong in this case?
UPD:
Here is a description of the original problem:
For example I have resource that returns all users and it also can return a subresource for particular user:
#Path("users") public class UsersResource {
#GET List<User> getUsers() {...}
#Path("{id}") UserSubresource getById(PathParam("id") String id) {
return new UserSubresource(getUserById(id));
}
}
Here is UserSubresource:
public class UserSubresource {
User user;
#GET public User getUser() {return user;}
...
public boolean isAccessible() {
return user.isAccessibleByCurrentUser();
}
}
And I need to check access rights for getUser() method, I can't check permissions before I find out actual user.
I understand that I can add this check in getUser() method itself, but this situation is the same for some other entities in my application, so I want more common solution.

Categories