Using SiteMesh with RequestDispatcher's forward() - java

I'm attempting to integrate SiteMesh into a legacy application using Tomcat 5 as my a container. I have a main.jsp that I'm decorating with a simple decorator.
In decorators.xml, I've just got one decorator defined:
<decorators defaultdir="/decorators">
<decorator name="layout-main" page="layout-main.jsp">
<pattern>/jsp/main.jsp</pattern>
</decorator>
</decorators>
This decorator works if I manually go to http://example.com/my-webapp/jsp/main.jsp. However, there are a few places where a servlet, instead of doing a redirect to a jsp, does a forward:
getServletContext().getRequestDispatcher("/jsp/main.jsp").forward(request, response);
This means that the URL remains at something like http://example.com/my-webapp/servlet/MyServlet instead of the jsp file and is therefore not being decorated, I presume since it doesn't match the pattern in decorators.xml.
I can't do a <pattern>/*</pattern> because there are other jsps that do not need to be decorated by layout-main.jsp. I can't do a <pattern>/servlet/MyServlet*</pattern> because MyServlet may forward to main.jsp sometimes and perhaps error.jsp at other times.
Is there a way to work around this without expansive changes to how the servlets work? Since it's a legacy app I don't have as much freedom to change things, so I'm hoping for something configuration-wise that will fix this.
SiteMesh's documentation really isn't that great. I've been working mostly off the example application that comes with the distribution. I really like SiteMesh, and am hoping I can get it to work in this case.

My understanding is that SiteMesh is integrated into the application as a Servlet filter. By default, servlet filters are only invoked against the original incoming request (in your case, the request to the servlet). Subsequent forward or include requests are not passed throuh the filter, and therefore will not be passed through sitemesh.
You can, however, instruct the filter to be invoked on forwards, using something like this:
<filter-mapping>
<filter-name>sitemesh</filter-name>
<servlet-name>MyServlet</servlet-name>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
Which instructs the container to only operate on FORWARD requests. The other options are INCLUDE and REQUEST, you can have several elements.
So your options are to either change your filter config to specify FORWARD, or to change your filter-mapping to match the servlet path, rather than the JSP path. Either one should work.

Related

How to make a servlet handle all URLs except JSPs [duplicate]

The familiar code:
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
My understanding is that /* maps to http://host:port/context/*.
How about /? It sure doesn't map to http://host:port/context root only. In fact, it will accept http://host:port/context/hello, but reject http://host:port/context/hello.jsp.
Can anyone explain how is http://host:port/context/hello mapped?
<url-pattern>/*</url-pattern>
The /* on a servlet overrides all other servlets, including all servlets provided by the servletcontainer such as the default servlet and the JSP servlet. Whatever request you fire, it will end up in that servlet. This is thus a bad URL pattern for servlets. Usually, you'd like to use /* on a Filter only. It is able to let the request continue to any of the servlets listening on a more specific URL pattern by calling FilterChain#doFilter().
<url-pattern>/</url-pattern>
The / doesn't override any other servlet. It only replaces the servletcontainer's built in default servlet for all requests which doesn't match any other registered servlet. This is normally only invoked on static resources (CSS/JS/image/etc) and directory listings. The servletcontainer's built in default servlet is also capable of dealing with HTTP cache requests, media (audio/video) streaming and file download resumes. Usually, you don't want to override the default servlet as you would otherwise have to take care of all its tasks, which is not exactly trivial (JSF utility library OmniFaces has an open source example). This is thus also a bad URL pattern for servlets. As to why JSP pages doesn't hit this servlet, it's because the servletcontainer's built in JSP servlet will be invoked, which is already by default mapped on the more specific URL pattern *.jsp.
<url-pattern></url-pattern>
Then there's also the empty string URL pattern . This will be invoked when the context root is requested. This is different from the <welcome-file> approach that it isn't invoked when any subfolder is requested. This is most likely the URL pattern you're actually looking for in case you want a "home page servlet". I only have to admit that I'd intuitively expect the empty string URL pattern and the slash URL pattern / be defined exactly the other way round, so I can understand that a lot of starters got confused on this. But it is what it is.
Front Controller
In case you actually intend to have a front controller servlet, then you'd best map it on a more specific URL pattern like *.html, *.do, /pages/*, /app/*, etc. You can hide away the front controller URL pattern and cover static resources on a common URL pattern like /resources/*, /static/*, etc with help of a servlet filter. See also How to prevent static resources from being handled by front controller servlet which is mapped on /*. Noted should be that Spring MVC has a built in static resource servlet, so that's why you could map its front controller on / if you configure a common URL pattern for static resources in Spring. See also How to handle static content in Spring MVC?
I'd like to supplement BalusC's answer with the mapping rules and an example.
Mapping rules from Servlet 2.5 specification:
Map exact URL
Map wildcard paths
Map extensions
Map to the default servlet
In our example, there're three servlets. / is the default servlet installed by us. Tomcat installs two servlets to serve jsp and jspx. So to map http://host:port/context/hello
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Doesn't match any extensions, next.
Map to the default servlet, return.
To map http://host:port/context/hello.jsp
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Found extension servlet, return.
Perhaps you need to know how urls are mapped too, since I suffered 404 for hours. There are two kinds of handlers handling requests. BeanNameUrlHandlerMapping and SimpleUrlHandlerMapping. When we defined a servlet-mapping, we are using SimpleUrlHandlerMapping. One thing we need to know is these two handlers share a common property called alwaysUseFullPath which defaults to false.
false here means Spring will not use the full path to mapp a url to a controller. What does it mean? It means when you define a servlet-mapping:
<servlet-mapping>
<servlet-name>viewServlet</servlet-name>
<url-pattern>/perfix/*</url-pattern>
</servlet-mapping>
the handler will actually use the * part to find the controller. For example, the following controller will face a 404 error when you request it using /perfix/api/feature/doSomething
#Controller()
#RequestMapping("/perfix/api/feature")
public class MyController {
#RequestMapping(value = "/doSomething", method = RequestMethod.GET)
#ResponseBody
public String doSomething(HttpServletRequest request) {
....
}
}
It is a perfect match, right? But why 404. As mentioned before, default value of alwaysUseFullPath is false, which means in your request, only /api/feature/doSomething is used to find a corresponding Controller, but there is no Controller cares about that path. You need to either change your url to /perfix/perfix/api/feature/doSomething or remove perfix from MyController base #RequestingMapping.
I think Candy's answer is mostly correct. There is one small part I think otherwise.
To map host:port/context/hello.jsp
No exact URL servlets installed, next.
Found wildcard paths servlets, return.
I believe that why "/*" does not match host:port/context/hello because it treats "/hello" as a path instead of a file (since it does not have an extension).
The essential difference between /* and / is that a servlet with mapping /* will be selected before any servlet with an extension mapping (like *.html), while a servlet with mapping / will be selected only after extension mappings are considered (and will be used for any request which doesn't match anything else---it is the "default servlet").
In particular, a /* mapping will always be selected before a / mapping. Having either prevents any requests from reaching the container's own default servlet.
Either will be selected only after servlet mappings which are exact matches (like /foo/bar) and those which are path mappings longer than /* (like /foo/*). Note that the empty string mapping is an exact match for the context root (http://host:port/context/).
See Chapter 12 of the Java Servlet Specification, available in version 3.1 at http://download.oracle.com/otndocs/jcp/servlet-3_1-fr-eval-spec/index.html.

How to make OpenSessionInViewFilter exclude static resources

I have implemented the OpenSessionInViewFilter for my MVC webapp and it works almost perfect. Only problem is that it also creates a session for every image, js, css etc that are requested from the webserver. This i dont want.
Im using struts2, spring and hibernate and this is my web.xml
<filter>
<filter-name>lazyLoadingFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>lazyLoadingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
So because i am mapping url-pattern /* it also takes all the images etc..
I tried setting it to *.jsp, and *.action, but then i get the lazyloading-exceptions again...
How should i do this? I've been looking for answers for 5 hours now and im getting a litle bit crazy in my head.
All i need to do is to make this filter IGNORE all the static resources. Thats it! And for everything else it can run. It sounds so simple, but its really really annoying me that i cant figure out how.
Any help would be greatly appriciated.
Do i need to extend the filter to write my own filter and exclude within it? And if so. How?
EDIT:
It seems like i could set up filter-mappings for my static files at the top of the filter-chain. And then send those to a "ByPassFilter", thus bypassing the filterchain for these static resources. Is this the way to go??
Thanks guys!
The general practice in such a scenario is to use the combination of Apache Web server with an application server (Tomcat/JBoss) with mod_jk module.
Here is link describing how to use this combination. (Another link)
The chief advantage of using this configuration is
Static content can be served by Apache web server.
The dynamic content requests (like *.jsp, *.action etc) are delegated to tomcat.
There are may other useful modules like content compression for static contents hence improving the response time.
Its more secure than the scenario wherein the app server is serving everything.
I understand this may not be precisely the solution you looking for, I suggested this as this is a general practice.
As far as your implementation of having a Bypassfilter is considered , if you have such a filter in front then once you skip the next filter in filter chain then basically the rest of filters in the chain will also get skipped (which doesn't seem to be a desirable thing to do in most situations ) . Also as the filter invocations for a request behave like
Filter1 -> Filter2 -->Struts Action/BL --> Filter 2 -->Filter 1
Hence the OpenSessionInViewFilter will kick in after the request is processed in your struts action ( which can be avoided by placing another bypass filter after open session in view filter in web.xml ) . However overall it has always appeared undesirable to me to skip the entire filter chain for skipping a single filter .
I haven't ever faced a need to skip OpenSessionInViewFilter ever , however if i had to do it then instead of having a Bypassfilter i would have a filter extending the OpenSessionInViewFilter filter which would be skipping my static resources from processing .
Include only those elements of the pattern that you need.
Something like this:
<filter-mapping>
<filter-name>lazyLoadingFilter</filter-name>
<url-pattern>*.html</url-pattern>
<url-pattern>/profile/edit</url-pattern>
<url-pattern>/cars/*</url-pattern>
</filter-mapping>
Just in case someone needs a solution with extending OpenSessionInViewFilter.
It prevents Hibernate session creation for pre-defined static resources.
/**
* Skips OpenSessionInViewFilter logic for static resources
*/
public class NonStaticOpenSessionInViewFilter extends OpenSessionInViewFilter {
static final Pattern STATIC_RESOURCES = Pattern.compile("(^/js/.*)|(^/css/.*)|(^/img/.*)|(^/fonts/.*)|(/favicon.ico)");
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String path = request.getServletPath();
if (STATIC_RESOURCES.matcher(path).matches()) {
filterChain.doFilter(request, response);
} else {
super.doFilterInternal(request, response, filterChain);
}
}
}
Basically Spring should have provided some property like excludePatterns for OpenSessionInViewFilter.
I totally agree with the answer from #Santosh.
the OpenSessionInViewFilter creates a resource and adds it the the ThreadLocal object, if the session is never used then the session is never actually created, this also means that a db connection is not used for that request. ( this is probably the answer to your question ).
If you still require to control things you can always create another filter extending the OpenSessionInViewFilter and execute the getSession method based on which resource is being called.

Modify the HTML of a servlet's errorPage from a Filter

I have a requirement to rewrite HTML generated by a web application. The requirement applies to all pages equally so naturally we went for a Filter.
I cribbed the stream wrapping approach from this Oracle documentation on filters and this works for most cases. Unfortunately, if the servlet throws an exception the flow of execution leaves my filter and the rewriting logic is not executed. This means the HTML of error pages is not modified.
I want to intercept the error page response as well. How do I do that?
Try adding this to your filter-mapping:
<dispatcher>FORWARD</dispatcher>
<dispatcher>ERROR</dispatcher>

Servlet - Dispatching request when the url-patern is like

I have some questions about dispatching a request in a servlet.
To sum it up, I deployed a website on a public server which forces me to have a url-pattern like /servlet/* for all my servlets (I heard that it was a default configuration anyway).
The problem is that while developping the application I didn't have such restrictions and therefore didn't built it to support such patterns....now, my application just doesn't work because of the urls. Let's say me servlet is declared and mapped like this :
<servlet>
<servlet-name>MainController</servlet-name>
<servlet-class>controller.controllers.MainController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>MainController</servlet-name>
<url-pattern>/servlet/MainController</url-pattern>
</servlet-mapping>
The problem is that I use this code in my Servlet :
this.getServletContext()
.getRequestDispatcher(destination).forward(request, response);
The destination parameter is always a *.jsp at the root of my webapp juste like "/index.jsp", "home.jsp", etc.
When I was using my application on localhost my servlet had this url pattern :
<url-pattern>/MainController</url-pattern>
and everything was working fine because the request dispatcher was always searching the .jsp at the root of the webapp. But now with my new url-pattern, it tries to serch all my .jsp at servlet/* just like servlet/index.jsp and, of corse throw me a
HTTP Status 404 - /servlet/index.jsp
I perfectly understand why it's acting like that as, if I recall well, Servlets cannot extend outside their current context.
But my question is, am I doomed ? Isn't there a way to tell to the request dispatcher to go to the .jsp I'm asking without taking care of the "/servlet/*" pattern ?
I absolutely need the request's object because I work with it before forwarding it.
I do really don't know how to get through this, so I'm seeking some help here hoping that someone had already faced this situation or at least have a clearer vision of the situation than me.
Thank's for taking the time to read this and for helping me.
Best regards,
Sampawende.
So, destination doesn't start with / which makes its location to be dependent on the path of the calling servlet?
Fix it accordingly:
request.getRequestDispatcher("/" + destination).forward(request, response);
By the way, if you'd like to prevent direct access to JSP as well (enduser could change the URL to point to the JSP without calling the controller first), then consider placing the JSPs in /WEB-INF folder. Don't forget to change the RequestDispatcher path accordingly: "/WEB-INF/" + destination.

JSP:forward Question

I am just getting started on web app development. I have an index.jsp that has just ONE line.
< jsp:forward page="landing.do?"/>
What does
the above line do?
page="landing.do?" actually refer to?
what does the question mark "?" next to "landing.do?" signify?
As Bozho rightly pointed out, a servlet called "action" is mapped to handle "*.do" in my web.xml (as shown below).
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
Now
How do I find out what the servlet "action" corresponding to "landing.do" actually does?
Thanks.
The <jsp:forward> forwards a client request to the url declared on the page attribute.
I also need to mention that in your example, you should have a / as first character inside your page declaration, if you want to specify a relative URL, i.e.:
This, in effect, is translated as a redirection to (if localhost)
http://localhost:8080/MyAPP/landing.do? (yours would have been translated to http://localhost:8080/MyAPPLanding.do?)
The ? allows you to append application/x-www-form-urlencoded parameters into your declaration.
More info here.
To know what landing.do does, do the following:
Go to your struts-config.xml (found in WEB-INF folder in your project) file and find any action (<action>) that a path="/landing") attribute.
Once you find your action, there's an attribute called type (inside that action). The type is a the class name of the action class that Struts calls to execute the action. The class name is fully qualified name.
Open the java file of the class (if it exists) and depending on the action (Action, DispatchAction, LookupDispatchAction), you will have to find its mappings and see what method Struts invokes.
In your example, my assumption will be based that your landing.do is of type Action. Therefore, read what the execute() method does. All actions actually, is execute() by Struts. The other actions are just Template Method patterns that knows what method to call by some mapping.
you probably have a servlet mapped to handle *.do in your web.xml
the ? means nothing here - generally it marks the start of get parameters (like ?param=value)
forward changes the current page with the specified, without the client knowing the change has happened.
This line will forward user to another page of the site, in particular to landing.do
page="landing.do?" actually refer to some page of the site landing.do. I believe this page is written with Struts framework. (But can be other)
what does the question mark "?" next to "landing.do?" mean nothing in this case. Generally after "?" there should be a list of request parameters. In this cases there will just be no parameters.
Update:
You should find servlet class which is mapped to that servlet name. After that you will be able to try to understand what that servlet class does. Also, look at the Struts tutorials or specification to get understanding of Struts framework workflows.

Categories