Question

I am using one class that handles @Path( "/{site}/group" ) and another one that handles @Path( "/{site}/user" ). I have one exception handler for the invalid {site} object in the context that returns status 404, but in order to validate the {site} I need to repeat the code on every method I use it. I am getting the site from the database and checking if comes null (then the exception is thrown).

Is there any built-in feature in JAX-RS that could help me to test the {site} context before the methods execution, then I could make some DRY out of this mess? If not what would be the best approach in this case?

EDIT:
(non functional snipped expressing my problem)

Using @Path( "/{site}/user" ) at class level

Then a method:

@GET
public void getUser( String site ) throws ... {
  Site site = findSiteFromDatabase( site );
  if ( site == null ) throw new InvalidException( "Invalid site" );

  ...
}

The problem with that approach is that I would have to test for the object validity in every method I create.
It would be very handy an utility that allows me to load the context object once (consider multiple classes using the same approach).

EDIT 2:

My root resource is an EJB, I need this to load the Site object from database (using JPA and stuff)

I used an EJB interceptor. The problem with this approach is that I have to always create a method with the fixed arguments @PathParam( "site" ) String site and Site site (the second argument is for not consulting the database twice).

    @AroundInvoke
    public Object initSite( InvocationContext context ) throws Exception {
        String siteName = context.getParameters()[ 0 ].toString();
        SiteEntity siteEntity = siteDAO.findSiteByPath( siteName );
        if ( siteEntity == null ) {
            throw new APINotFoundException( "The site you are accessing does not exist" );
        }

        Object[] params = new Object[]{ siteName, siteEntity };
        context.setParameters( params );

        return context.proceed();
    }

    @GET
    @Path( "/user" )
    public UserData getUser(
        @PathParam( "site" ) String site,
        SiteEntity site
    ) {
        return ...
    }

Any approach not requiring me to build a fixed signature is welcome.

EDIT 3: My solution above did not worked. The EJB interceptor is not able to honor my ExceptionMapper declaration (It always returns status 500). I am almost giving up, did anyone got into the same problem?

Was it helpful?

Solution

As far I know, there isn't no such a default behavior in JEE. This is how I would solve this issue.

  1. Adapt your restful endpoint URL(s) to: (You will need this since the usage of regular exprssion for the <url-pattern> tags is very limmited!)

    @Path( "/user/{site}")

    @Path( "/group/{site}")

  2. Create a User and a Group request filter sth. like: (Just pseudo source!!!)

    public class UserFilter implements Filter { 
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    
    }
    
    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
            FilterChain chain) throws IOException, ServletException {
    
        HttpServletRequest req = null;
        boolean valid = false;
        PrintWriter out = null;
    
        if (request instanceof HttpServletRequest) {
    
            req = (HttpServletRequest) request;
            // TODO extract site info from url ...
            // TODO perform check ...
            // valid = ...
        }
    
        if (valid) {
            chain.doFilter(request, response);
        } else {
    
            response.setContentType("text/html");
            out = response.getWriter();
            out.println("<html><head><title>Your specific response</title></head><body>");
            out.println("<h2>Sorry, Unknown URL!</h2>");
    
            out.println("</body></html>");
            out.close();
        }
    }
    
    @Override
    public void destroy() {
    
    }
    

    }

  3. Add your filter in web.xml:

    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        version="2.5">
        ...
        <filter>
            <filter-name>User validation fiter</filter-name>
            <filter-class>path.to.your.UserFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>User validation fiter</filter-name>
            <url-pattern>/services/user/*</url-pattern>
        </filter-mapping>
    
        <filter>
            <filter-name>Group validation fiter</filter-name>
            <filter-class>path.to.your.GroupFilter</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>Group validation fiter</filter-name>
            <url-pattern>/services/group/*</url-pattern>
        </filter-mapping>
        ...
    </web-app>
    

OTHER TIPS

I can see tow option:

BeanParm (need JAX-RS 2.0, possible to upgrade JAX-RS in JBoss 7):

public SiteBean{

   @PathParam
   private String site;

   public Site getSite() throws WebApplicationException{
       Site s = findSiteFromDatabase( site );
       if ( s == null ) throw new WebApplicationExcepiton();
       // you can customize the Responce in the WebApplicationException
   }
}

In every method requesting the site you need to do:

@GET
public void getUser( @BeanParam SiteBean site ){
      Site s = site.getSite();
}

Other option is to use sub-resources.

@Path("{site}")
public SiteResource{

    private Site site;

    public SiteResource(@PathParam String site) throws new WebApplicationExcepiton{
         this.site = = findSiteFromDatabase( site );
         if ( s == null ) throw new WebApplicationExcepiton();
       // you can customize the Responce in the WebApplicationException
    }

    @Path("user")
    public UserResource user(){
         return new UserResource(site);
    }

    ... // same for other sub resource.
}

The User resource:

public UserResource{
    private Site site;

    public UserResource(Site site){
        this.site = site;
    }

    @GET
    public void getUser{
       ...
    }

    ...
}

The last one should work with JAX-RS 1.1.

Why not throwing an exception from your findSiteFromDatabase( site ) method instead of returning a null value ? (btw, JPA has a similar behaviour with Query#getSingleResult() : http://docs.oracle.com/javaee/6/api/javax/persistence/Query.html#getSingleResult%28%29 ).

Then, you could use an JAX-RS ExceptionMapper to convert the exception into a JSON/XML/HTML response with the proper return code and body, depending on your needs.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top