Multiple endpoints with Resteasy Multiple endpoints with Resteasy spring spring

Multiple endpoints with Resteasy


actually you can. After few hours of debugging I came up with this:

1) Declare multiple resteasy servlets in your web.xml (two in my case)

<servlet>    <servlet-name>resteasy-servlet</servlet-name>    <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>    <init-param>        <param-name>resteasy.servlet.mapping.prefix</param-name>        <param-value>/openrest</param-value>    </init-param>           <init-param>        <param-name>resteasy.resources</param-name>        <param-value>com.mycompany.rest.PublicService</param-value>    </init-param></servlet>    <servlet>    <servlet-name>private-resteasy-servlet</servlet-name>    <servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>    <init-param>        <param-name>resteasy.servlet.mapping.prefix</param-name>        <param-value>/protectedrest</param-value>    </init-param>           <init-param>        <param-name>resteasy.resources</param-name>        <param-value>com.mycompany.rest.PrivateService</param-value>    </init-param></servlet><servlet-mapping>    <servlet-name>private-resteasy-servlet</servlet-name>    <url-pattern>/protectedrest/*</url-pattern></servlet-mapping>      <servlet-mapping>    <servlet-name>resteasy-servlet</servlet-name>    <url-pattern>/openrest/*</url-pattern></servlet-mapping>  

Please pay attention to the fact that we initialize personal resteasy.servlet.mapping.prefix and resteasy.resources for each our servlet.Please don't forget to NOT include any botstrap classes as filters or servlets! And disable autoscan as well.

2) Create a filter that cleans up application from the RESTeasy's global information that it saves in context:

public class ResteasyCleanupFilter implements Filter {    @Override    public void init(FilterConfig filterConfig) throws ServletException {        // TODO Auto-generated method stub    }    @Override    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,            ServletException {        request.getServletContext().setAttribute(ResteasyProviderFactory.class.getName(), null);        request.getServletContext().setAttribute(Dispatcher.class.getName(), null);        chain.doFilter(request, response);    }    @Override    public void destroy() {        // TODO Auto-generated method stub    }}

Register it for any request to your services (here I used it for all requests for simplisity):

<filter>    <filter-name>CleanupFilter</filter-name>    <filter-class>com.mycompany.ResteasyCleanupFilter</filter-class></filter><filter-mapping>    <filter-name>CleanupFilter</filter-name>    <url-pattern>/*</url-pattern></filter-mapping> 

Thats it!Now you have two different REST services which lays under different prefixes : /openrest which meant to service all public requests and /protectedrest that takes care about all the private stuff in the app.

So why does it work (or why it does not work otherwise)?

When you call openrest instance for the first time it tries to initalize itself and when done saves the state in the global servletContext like this :

 servletContext.setAttribute(ResteasyProviderFactory.class.getName(), deployment.getProviderFactory()); servletContext.setAttribute(Dispatcher.class.getName(), deployment.getDispatcher());

And if you will let it be your call to your second /protectedrest will get the SAME configuration! That is why you need to clean up this information some where. That is why we used our CleanupFilter which empty the context so brand new rest servlet could initialize itself with all the init parameters we declared.

This is a hack, but it does the trick.

This solution was tested for RESTEasy 2.3.6

EDITED

Works with 3.0.9.final as well!


AFAIK, you cannot have multiple servlet mappins for your JAX-RS implementation. What you could do is: map RESTEasy to '/' (or '/api' for example if your application has other resources to serve and you don't want the JAX-RS part to interfere), then have the following @Path annotations:

@Path("/public/people")public interface PeopleService {  // Stuff}@Path("/internal/management")public interface ManagementService {  // Stuff}