4

I have a number of Java Server Pages set up already and I would like to use a Controller/View system by adding a Process Servlet (which extends HttpServlet).

I just want to basically process the requested JSPs as normal after the ProcessServlet has added some attributes.

Say all my JSPs are in a directory called /content/ and my web.xml file has a rule to map /content/*.jsp to my ProcessServlet

I have not been able to find a way short of moving all of my JSPs into a different directory (/content-JSPs/) so that they can be dispatched to without having to go through the ProcessServlet endlessly.

Is there a way to basically dispatch#forward() (some other method?) to the requested JSP without having it go through the ProcessServlet again?

It's a bit hard to believe that this lack of flexibility exists. Why can't the Servlet just act as a pass through to the JSP?

My goal here is to set everything up so that the web server does not have to have a separate directory for all JSPs and another directory for everything else i.e. CSS, JavaScript and images. I would like to keep the directory structure (and URL structure) as it is.

Matthew
  • 8,183
  • 10
  • 37
  • 65
  • If you don't want your JSPs directlymaccessiblemthey should be under WEB-INF anyway. Are you trying to map to identical URLs for a reason? – Dave Newton Nov 10 '11 at 01:28
  • I know it might be a bit OCD but I would like to keep the directory structure if possible. I like that my URLs map directly to the actual server directory (well close enough anyway). – Matthew Nov 10 '11 at 01:53
  • 1
    ... You can't have it both ways. Put the JSPs under /WEB-INF/content. – Dave Newton Nov 10 '11 at 02:13
  • It makes enough logical sense to have it work this way. Simpler is better, no? – Matthew Nov 10 '11 at 02:16
  • 1
    No. Exposing the JSPs directly when they depend on servlet code to run before they're hit is wrong. Trying to map a virtual resource to the same path as a physical resource makes things *more* complicated, and less communicative--you see the JSP and assume it's a JSP, but it's not. – Dave Newton Nov 10 '11 at 02:24
  • But they only become virtual resourses because of the fact that the Servlet touches them, right? Before that they were physical enough. The Servlet accomplishes the job of a central location where I can set up things like a user session for all my content but then convolutes things by making me change my directory structure. – Matthew Nov 10 '11 at 02:31
  • If it were simpler this way, you wouldn't have to ask the question, and not everyone would have said the same thing. Good luck! – Dave Newton Nov 10 '11 at 02:38
  • So basically if there was a method like dispatch that did exactly what I was wanting then I would have found it by now. Got it. – Matthew Nov 10 '11 at 02:42
  • There's RequestDispatcher.forward, which is how I assumed your servlet was serving up the JSP in the first place. – Dave Newton Nov 10 '11 at 02:45
  • Yes, RequestDispatcher.forward is what I meant. RequestDispatcher.include doesn't even work. I need RequestDispatcher.justProcessTheJSPAndDoNotRecurse – Matthew Nov 10 '11 at 02:53
  • You just need to structure your webapp as JEE intends, IMO. You're not making things simpler, really. – Dave Newton Nov 10 '11 at 03:07
  • IMO JEE should intend for someone to want to do this. Like I said, it's a simple request. Adding new functionality (a Servlet) should not necessarily add complexity. – Matthew Nov 10 '11 at 20:25
  • And it doesn't, when you follow normal JEE conventions. Whenever you have two things named the same thing, it's inherently complex. – Dave Newton Nov 10 '11 at 20:37
  • Your web application was from the beginning on just badly structured. You should fix it, not workaround it. – BalusC Nov 13 '11 at 14:13
  • Agreed but you'd have to see the code to know just how bad. This is just band-aids until we can re-design the whole thing using MVC. – Matthew Nov 13 '11 at 14:19

5 Answers5

5

Put them in /WEB-INF folder. This also effectively hides JSPs away from direct access. You only need to change the RequestDispatcher#forward() call to include /WEB-INF in the path.

request.getRequestDispatcher("/WEB-INF/content" + request.getPathInfo()).forward(request, response);

Please note that the URL pattern of /content/*.jsp is syntactically invalid. It should be /content/*. Perhaps you have also really used that. To skip static resources like images/css/JS, you should just not put them in /content, but in for example /resources, /static, etc.

Related:

Community
  • 1
  • 1
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Yes, this would work too, unless you need to have direct access to some jsp page, in which case only move the jsp's that are referenced by your controller (ProcessServlet). – Crollster Nov 10 '11 at 01:31
  • Won't /content/* make all requests for resources pass through the Servlet? I have css and images under there as well that don't need any sort of pre-processing. – Matthew Nov 10 '11 at 01:48
  • I know it might be a bit OCD but I would like to keep the directory structure if possible. I like that my URLs map directly to the actual server directory (well close enough anyway). – Matthew Nov 10 '11 at 01:51
  • Just put them in another folder? Otherwise change URL pattern to `*.jsp` if you need all JSPs to be preprocessed. Otherwise you really can't go around some conditional checks. – BalusC Nov 10 '11 at 01:51
  • I wouldn't want any kind of performance hit for resources other than JSPs such as css, JavaScript and images. – Matthew Nov 10 '11 at 02:08
  • I would upvote this since it seems to be the closest solution but I need more reputation. – Matthew Nov 10 '11 at 02:09
3

You can also use Sevlet Filter instead of Servet. This is a good option if your servlet only adds some parameters to request. And you don't have to manually dispatch your request to JSP.

Igor Nikolaev
  • 4,597
  • 1
  • 19
  • 19
  • It depends, as for MVC I would consider using Spring MVC – Igor Nikolaev Nov 10 '11 at 02:06
  • @user1038812 According to the spec, filters aren't supposed to serve resources, just filter. This has caused some problems in the Struts 2 world. His point was that if all you're doing is putting stuff in session/etc. a filter is another option-but it shouldn't act as a controller. – Dave Newton Nov 10 '11 at 02:40
  • Nevertheless some new frameworks tend to use filters instead of servlets :) For example Apache Wicket, but it's a different story :) – Igor Nikolaev Nov 10 '11 at 02:49
  • @Igor Nikolaev MVC was not considered when the app was first built so using Spring would require a re-architecture. It's not a bad idea though. – Matthew Nov 10 '11 at 03:06
  • +1 Actually, this is a good suggestion. If, Like you say, the OP is only doing simple things. :) – Crollster Nov 10 '11 at 03:24
  • @Igor Nikolaev, Struts2, Apache Wicket, etc. uses `Filter` as controller, so it can be done. – Buhake Sindi Nov 17 '11 at 05:42
1

Why not, instead of mapping to *.jsp, map to something like *.page (or whatever term you like), and then your process servlet can do its processing, and replace the .page with .jsp and instruct the RequestDispatcher to forward() to that page.

As long as all links on the pages, that you wish to go through the ProcessServlet, use the .page name, then it will probably work.

Crollster
  • 2,751
  • 2
  • 23
  • 34
  • Since the JSPs already exist, there are a lot of links throughout the site that refer to them as page.jsp (for example). This would require changing all links to them on the site. I wonder if this would work in reverse. Could I change all the JSPs to the .page extension and have the Servlet translate .jsp into .page? – Matthew Nov 10 '11 at 01:41
  • @user1038812 You can make the container use whatever extension you want, if you don't care about following web app conventions. – Dave Newton Nov 10 '11 at 02:27
1

As many have suggested its better to use Filters in this case.

Put following snippet into web.xml

Filter definition

<filter>
    <filter-name>ProcessFilter</filter-name>
    <filter-class>my.filter.ProcessFilter</filter-class>
</filter>

Filter mapping

<!-- Map all ".jsp" that should go through the filter-->
<filter-mapping>
    <filter-name>ProcessFilter</filter-name>
    <url-pattern>/content/*.jsp</url-pattern>
</filter-mapping>

<!-- If you have Any servlets that needs to go through ProcessFilter -->
<filter-mapping>
    <filter-name>ProcessFilter</filter-name>
    <servlet-name>MyServlet</servlet-name>
</filter-mapping>

OncePerRequestFilter

If you would want to execute the filter only once you could store an attribute in request scope for the first time, and next time you could check if the attribute is set in which case do not process further.

If you are using Spring framework you can either use one of the sub classes of OncePerRequestFilter or extend it and just implement doFilterInternal().

Otherwise you could refer to OncePerRequestFilter.java : raw and implement/extend your filter.

Here is a simplified version of it.

 public class ProcessFilter extends Filter {

public final void doFilter(ServletRequest request, ServletResponse response, 
           FilterChain filterChain)
        throws ServletException, IOException {

    if (!(request instanceof HttpServletRequest) || 
                !(response instanceof HttpServletResponse)) {
        throw new ServletException("OncePerRequestFilter just supports HTTP requests");
    }
    HttpServletRequest httpRequest = (HttpServletRequest) request;
    HttpServletResponse httpResponse = (HttpServletResponse) response;

    String alreadyFilteredAttributeName = "ALREADY_PROCESSED_BY_PROCESS_FILTER";
    if (request.getAttribute(alreadyFilteredAttributeName) != null) {
        // Proceed without invoking this filter...
        filterChain.doFilter(request, response);
    }
    else {
        // Do invoke this filter...
        request.setAttribute(alreadyFilteredAttributeName, Boolean.TRUE);
        try {
            doFilterInternal(httpRequest, httpResponse, filterChain);               
        }
        finally {
            // Remove the "already filtered" request attribute for this request.
            request.removeAttribute(alreadyFilteredAttributeName);
        }
    }
}

    protected void doFilterInternal(
        HttpServletRequest request, HttpServletResponse response, 
                    FilterChain filterChain) {
        throws ServletException, IOException
                            /*
                             *
                             *  
                             *  Put your processing logic here
                             *
                             *
                             */
    }

}
Prashant Bhate
  • 10,907
  • 7
  • 47
  • 82
0

According to me you should try using <filter> in your web.xml to make some of your requests bypass the servlet.

Japan Trivedi
  • 4,445
  • 2
  • 23
  • 44