3

I would like to map my servlet to /*, but it failed with an infinite loop.

<servlet>
    <servlet-name>helloServlet</servlet-name>
    <servlet-class>my.HelloServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
    <servlet-name>helloServlet</servlet-name>
    <url-pattern>/*</url-pattern>
</servlet-mapping>

The java code is:

public class HelloServlet extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response){
        request.getRequestDispatcher("/WEB-INF/jsps/hello.jsp").forward(request, response);
    }

}

If I map to /hello, everything works fine.

As the HelloServlet is mapped to /*, it will also be invoked on RequestDispatcher#forward() and cause the infinite loop.

How is this caused and how can I solve it?

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
donnior
  • 1,055
  • 1
  • 9
  • 12
  • `*` is a wildcard. What kind of urls are you trying to match with your pattern? – Andrew Logvinov Dec 18 '12 at 14:20
  • This is just demo, what I want to implement is a simple web framework using "The Front-end Controller" design patten, so this servlet must catch wildcard request and dispatch them to their separate controller, just like SpringMVC – donnior Dec 18 '12 at 14:30

2 Answers2

5

This is not possible. The JSP should actually invoke container's builtin JspServlet. But the /* mapping definied by the webapp has a higher precedence.

You need to map the servlet on a more specific URL pattern like /pages/* and create a servlet filter which forwards non-static requests to that servlet. Yes, non-static requests (image/CSS/JS files) are also covered by /*, but they should not be processed by the servlet at all.

Assuming that you've all static resources in /resources folder, the following should do:

<filter>
    <filter-name>filter</filter-name>
    <filter-class>com.example.Filter</filter-class>
</filter>
<filter-mapping>
    <filter-name>filter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

<servlet>
    <servlet-name>controller</servlet-name>
    <servlet-class>com.example.Controller</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>controller</servlet-name>
    <url-pattern>/pages/*</url-pattern>
</servlet-mapping>

With the following in filter's doFilter():

HttpServletRequest req = (HttpServletRequest) request;
String path = req.getRequestURI().substring(req.getContextPath().length());

if (path.startsWith("/resources")) {
    chain.doFilter(request, response); // Goes to container's own default servlet.
} else {
    request.getRequestDispatcher("/pages" + uri).forward(request, response); // Goes to controller servlet.
}

This takes place fully transparently without any change to /pages in the URL. The forward to the JSP will not trigger the filter or the servlet. Filters do by default not kick in on forwards and the JSP forward path does not match the controller servlet's URL pattern any more.

Alternatively, if you have your own default servlet implementation, then you could map the servlet to / and let it delegate to the default servlet if the request isn't applicable as a front controller request. This is what Spring MVC is doing under the covers. Creating a default servlet is however not a trivial task as it should be capable of responding to conditional requests, caching requests, streaming requests, resume requests, directory listing requests, etecetera.

See also:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks, in my demo their is no static resources; and because the servlet is one simple web framework's Front-end Controller, so it should mapping to /*. SpringMVC already can mapping to /*, and it has separate solution for static resources like yours, but it don't use filter. – donnior Dec 18 '12 at 14:35
  • Then remove the static resource check from the filter and let it always forward. Note that static resource checking is unrelated to your concrete problem. It's just covered in the answer because that's the normal real world case. I didn't expect your "design" to be off from that (a webapp without any CSS/JS/image resources is quite strange these days). – BalusC Dec 18 '12 at 14:36
  • I run my code under jetty use maven, didn't test it with Tomcat. But I think it's not the reason. – donnior Dec 18 '12 at 14:40
  • I'm not sure why you're talking about Jetty vs Tomcat. This problem is unrelated to the container used and I've never mentioned any container-related specifics. – BalusC Dec 18 '12 at 14:42
  • Just clarify my env. Now I'm trying to figure it out why SpringMVC can mapping to /* – donnior Dec 18 '12 at 14:44
  • sorry, seems SpringMVC is mapping to / not /*, maybe this is the reason. – donnior Dec 18 '12 at 14:51
  • This one acts then as *default servlet* which means that when the request did not match any of the registered servlets, then it will end up in the default servlet. See also the 1st "See also" link. This has the disadvantage that any static resources needs to be handled *yourself*, including caching and range requests (media streaming!). – BalusC Dec 18 '12 at 15:11
-1

This is possible duplicate to Servlet Filter going in infinite loop when FORWARD used in mapping in JSF

Otherwise should check what your JSP contains, it could be that it makes request for css or image files that can result in this behaviour. Also would recommend to try without a wildcard.

Community
  • 1
  • 1
Eds
  • 781
  • 1
  • 5
  • 7
  • 1
    CSS/image files are not processed in the same HTTP request at all. This assumption makes no sense and hence this answer is invalid. If you're unsure about an answer, just experiment it yourself first so that you can reliable answer straight from experience. Otherwise just leave a comment. The "possible duplicate" concerns a completely different problem. – BalusC Dec 18 '12 at 14:30