pnyota
pnyota

Reputation: 65

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.

Upvotes: 0

Views: 494

Answers (1)

Nico Van Belle
Nico Van Belle

Reputation: 5166

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.

Upvotes: 2

Related Questions