This is probably not so much a mistake as a limitation of the Servlet specification. The information on how JAX-RS @ApplicationPath implementation-specific, and I can't speak for all implementations, but I assume a typical approach is to just use it as a servlet URL pattern. As an example, consider the implementation of the Jersey ServletContainerInitializer, you will find that the addServletWithApplication() method is responsible for creating the servlet and mapping for processing requests, and you can make sure that it really uses the path from @ApplicationPath as the route set by the Jersey ServletContainer.
Unfortunately, from time immemorial, the Servlet specification allows only a small number of ways to map servlets to URLs. The current parameters from servlet 3.0, given in Section 12.2 of the specification , are available only as PDF, therefore they are not related to each other in sections:
/.../* , where the initial /... is zero or more path elements*.<ext> where <ext> is some extension to match- empty string that displays only empty pool / root context
/ , the only slash that points to the default servlet in a context that handles anything that doesn't match anything else.- any other string that is treated as a literal value to match
In the same section of the specification, there are also certain rules for the order in which matching rules should be applied, but the short version is this: in order for your resource class response requests to be in the root of the context, you must use either / or /* as the path. If you use / , you replace the default servlet container, which is usually responsible for handling static resources. If you use /* , then you make it too greedy and say that it must match all the time, and the default servlet will never be called.
So, if we agree that we are inside the field defined by the constraints of the servlet URL patterns, our options are quite limited. Here are the ones I can think of:
1) Use @ApplicationPath("/") and explicitly map your static resources by name or extension to the default servlet in the container (with the name "default" in Tomcat and Jetty, not sure about others). In web.xml it will look like
<servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/index.html</url-pattern> </servlet-mapping>
or with ServletContextInitializer e.g.
public class MyInitializer implements ServletContainerInitializer { public void onStartup(Set<Class<?>> c, ServletContext ctx) { ctx.getServletRegistration("default").addMapping("*.html"); ctx.getServletRegistration("default").addMapping("/index.html"); } }
Due to the way matching rules are written, the extension template wins the servlet by default, so you only need to add the map for the static file extension if there is no match between them and any "extensions" in your API. This is pretty close to the unwanted option mentioned in the forum post that you linked, and I just mentioned it for completeness and added the ServletContextInitializer part.
2) Leave your API mapped to /rest/* and use a filter to identify API requests and forward them to this path. This way you exit the servlet URL pattern window and can match the URLs in any way. For example, if you assume that all your REST calls refer to paths that begin with "/ foo" or exactly "/ bar", and all other requests should go to static resources, then something like:
import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.util.regex.Pattern; @WebFilter(urlPatterns = "/*") public class PathingFilter implements Filter { Pattern[] restPatterns = new Pattern[] { Pattern.compile("/foo.*"), Pattern.compile("/bar"), }; @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { if (request instanceof HttpServletRequest) { String path = ((HttpServletRequest) request).getServletPath(); for (Pattern pattern : restPatterns) { if (pattern.matcher(path).matches()) { String newPath = "/rest/" + path; request.getRequestDispatcher(newPath) .forward(request, response); return; } } } chain.doFilter(request, response); } @Override public void init(FilterConfig filterConfig) throws ServletException {} @Override public void destroy() {} }
With the above, you essentially translate queries as follows:
http://example.org/foo -> http://example.org/rest/foo http://example.org/foox -> http://example.org/rest/foox http://example.org/foo/anything -> http://example.org/rest/foo/anything http://example.org/bar -> http://example.org/rest/bar http://example.org/bart -> http://example.org/bart http://example.org/index.html -> http://example.org/index.html
3) Understand that the previous version basically rewrites the URL and uses an existing implementation such as Apache mod_rewrite , Tuckey rewrite filter or ocpsoft Rewrite .