Servlet + redirection - servlets

I need some suggestions. I have defined servlet mapping as
<servlet-mapping>
<servlet-name>My Servlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
However there are some static html files. So i have mapped them to the default servlet to serve the static html files
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
However, i want the user to have access to them only when the user has logged in. The welcome page is Login.html. If the user tries to access any other static file and has not logged in i.e there is not session then i should redirect user to the login page. But with current mapping the user is able to access index.html file as the request is served by default servlet.
Please suggest .

Your intent is to have a front controller servlet, not to replace the default servlet. So you should actually not be mapping your front controller servlet on /.
You should map the controller servlet on a more specific URL pattern, such as /app/*. To keep URLs transparent, your best bet is to create a filter which determines the request URI and continues the chain on static content like HTML and dispatches the remnant to the controller servlet.
E.g.
String uri = request.getRequestURI();
if (uri.endsWith(".html")) {
chain.doFilter(request, response);
} else {
request.getRequestDispatcher("/app" + uri).forward(request, response);
}

You can extend the DefaultServlet of your web server.The extended servlet will be your front controller. In the doGET or doPOST method forward your static pages to the super class. DefaultServlet is the servlet that is mapped to url "/" by default. I have used it with jetty server but it can be implemented in tomcat as well.
public class FrontController extends DefaultServlet {
#Override
public void init() throws UnavailableException {
super.init();
}
#Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException {
String uri = request.getRequestURI();
/*
* if request is trying to access inside /static then use the default
* servlet. YOU CAN USE YOUR OWN BUSINESS LOGIC TO FORWARD REQUESTS
* TO DEFAULTSERVLET
*/
if (uri.startsWith("/static/")) {
super.doGet(request, response);
return;
} else {
// else use your custom action handlers
}
}
}
In the above code samples I have forwarded all the requests starting with /static/ to the default servlet to process. In this way you can map the FrontController to "/" level .
<servlet>
<description></description>
<display-name>FrontController</display-name>
<servlet-name>FrontController</servlet-name>
<servlet-class>FrontController</servlet-class>
<servlet-mapping>
<servlet-name>FrontController</servlet-name>
<url-pattern>/</url-pattern>

Related

Spring 4 upgrade broke error page filter chain

Scenario:
We have an interceptor that looks for bogus attributes in URLs and throws a NoSuchRequestHandlingMethodException if it finds one. We then display a custom 404 page.
All pages go through the same filter chain to set up the local request state, log some information, and then display the requested page. In Spring 4, it stopped going through the filter chain for the 404 page in this case. It still goes through it if you go to a completely bogus page, and the 404 works, but when we throw the NoSuchRequestHandlingMethodException, the filters don't happen.
Spring 3:
1. Runs the filter chain for the main request
2. We throw NoSuchRequestHandlingMethodException
3. Filter chain finishes
4. New filter chain starts
5. We log the error page metrics
6. We display a nice 404 page to the customer
Spring 4:
1. Runs the filter chain for the main request
2. We throw NoSuchRequestHandlingMethodException
3. Filter chain finishes
4. We try to log the error page metrics, but NPE since a second filter chain never started
5. We display a terrible blank page to the customer
Filter code in web.xml:
<!-- The filter that captures the HttpServletRequest and HttpServletResponse-->
<filter>
<filter-name>ServletObjectFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<param-name>targetBeanName</param-name>
<param-value>xxxxxxx.servletObjectFilter</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>ServletObjectFilter</filter-name>
<servlet-name>springmvc</servlet-name>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
...
<error-page>
<error-code>404</error-code>
<location>/errors/404</location>
</error-page>
Filter code:
public void doFilterInternal( HttpServletRequest request, HttpServletResponse response, FilterChain chain )
throws ServletException, IOException {
try {
getServletContainer().setServletObjects( request, response );
chain.doFilter( request, response );
} finally {
getServletContainer().removeAll();
}
ServletContainer:
static final ThreadLocal< HttpServletRequest > REQUESTS = new ThreadLocal< HttpServletRequest >();
static final ThreadLocal< HttpServletResponse > RESPONSES = new ThreadLocal< HttpServletResponse >();
public void setServletObjects( HttpServletRequest request, HttpServletResponse response ) {
REQUESTS.set( request );
RESPONSES.set( response );
}
public void removeAll() {
REQUESTS.remove();
RESPONSES.remove();
}
Code that then fails:
public class RequestResponseAwareBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization( Object bean, String beanName ) {
...
if ( bean instanceof RequestAware ) {
HttpServletRequest request = getServletContainer().getRequest();
if ( request == null ) {
throw new IllegalStateException( "The request object is NULL" );
}
RequestAware requestAware = (RequestAware) bean;
requestAware.setRequest( request );
}
}
I "solved" the problem by splitting up my error page #Controller into two, one where they're the targets of internal redirects and don't get the filter chain, and one where they are directly loaded, and do get the filter chain. I then added the redirect #Controller to the interceptor blacklist, so it doesn't require any logic or data from the filters. It solved this specific problem, but I'm worried that something else in my codebase also relies on this behavior.

How can I redirect but keep the URL the same

I am doing the following, but the url in the address bar changes. from /test to localhost:8080...
Is it possible to keep the url the same in the address bar?
<servlet>
<servlet-name>test</servlet-name>
<servlet-class>xxx.xxxx.Servlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>test</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
Servlet.java
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String path = request.getRequestURI();
response.sendRedirect("http://localhost:8080"+path);
}
You can use forward instead of redirect. I wrote a method that gets a servlet's name and dispatch it:
protected void gotoServlet(HttpServletRequest req, HttpServletResponse resp,String servletName) throws ServletException, IOException {
RequestDispatcher dispatcher = this.getServletContext().getNamedDispatcher(servletName);
dispatcher.forward(req,resp);
}
You have to first understand that your Servlets (HttpServlet) and the Servlet container they're running in are implementing an HTTP stack. HTTP is a request-response protocol.
The client sends an HTTP request and the server (your Servlet container) replies with an HTTP response. In this case,
response.sendRedirect("http://localhost:8080"+path);
it is responding with a 302, indicating redirection. How your client handles this is up to them. Typically, a browser client will send a new HTTP GET request to the redirection target. This will force a page refresh/renew.
If that's not the behavior you want, you need to change your client behavior. For example, you can put part of your client logic within an iframe. You'd then have the redirect only refresh the iframe.

How to get Request object in CustomPhaseListener?

Am hitting my Servlet from a link. Some Cookies would have been already set in Client. When my Servlet is hit, I want to retrieve these Cookies.
For eg., am hitting the link like http:/myDomain/myServlet/ServletReceiver
In web.xml, I have below code
<servlet>
<display-name>ServletReceiver</display-name>
<servlet-name>ServletReceiver</servlet-name>
<servlet-class>(location of my ServletReceiver)</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletReceiver</servlet-name>
<url-pattern>/ServletReceiver</url-pattern>
</servlet-mapping>
And my ServletReceiver code is below
public class ServletReceiver extends HttpServlet{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
Cookie[] cookies = request.getCookies();
// Do some checks here based on cookies obtained and redirect to corresponding page
RequestDispatcher dispatcher=request.getRequestDispatcher("/pages/index.jsf");
dispatcher.forward(request, response);
}
}
My requirement is, when I retrieve some data from Cookies, I want to set it into bean. And since am creating an instance of the bean in CustomPhaselistener (and not in ServletReceiver), if I get the request object through which I can get cookie values then I can set that in my bean in PhaseListener.
My bean is request scoped.
So, is there a way to get request object in CustomPhaseListener?
Also, am retrieving Cookies in doGetmethod. Is that suggested?
Am using JSF 1.2

web.xml error-page location redirect is not going through my filter definitions

In my web.xml I've done the following error-page mappings, but when they are invoked those invoked requests are not passing through the filter definitions specified in web.xml file.
<error-page>
<error-code>403</error-code>
<location>/error.vm?id=403</location>
</error-page>
<error-page>
<error-code>400</error-code>
<location>/error.vm?id=400</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/error.vm?id=404</location>
</error-page>
<error-page>
<exception-type>javax.servlet.ServletException</exception-type>
<location>/servlet-exception.vm</location>
</error-page>
My application is using spring-mvc and I want to handle the handler not found condition from spring mvc. My application is an multi tenant application where some filters are responsible for setting some information related to the schema.
The requests are reaching in my error.vm controller but since they are passing through the filter I'm not able to determine the theme and SecurityContext etc.
How to solve this problem?
Thank you.
Instead of using web.xml's error pages you could use a servlet filter. The servlet filter could be used to catch all exceptions, or just a particular exception such as org.springframework.web.portlet.NoHandlerFoundException. (Is that what you mean by "handler not found" exception?)
The filter would look something like this:
package com.example;
import java.io.IOException;
import javax.servlet.*;
import javax.servlet.http.*;
import org.springframework.web.portlet.NoHandlerFoundException;
public class ErrorHandlingFilter implements Filter {
public void init(FilterConfig config) throws ServletException { }
public void destroy() { }
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
try {
chain.doFilter(request, response);
} catch (NoHandlerFoundException e) {
// Or you could catch Exception, Error, Throwable...
// You probably want to add exception logging code here.
// Putting the exception into request scope so it can be used by the error handling page
request.setAttribute("exception", e);
// You probably want to add exception logging code here.
request.getRequestDispatcher("/WEB-INF/view/servlet-exception.vm").forward(request, response);
}
}
}
Then, set this up in web.xml with the help of Spring's DelegatingFilterProxy:
<filter>
<filter-name>errorHandlingFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>errorHandlingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
And then finally, turn the filter into a spring bean inside your spring context xml:
<bean id="errorHandlingFilter" class="com.example.ErrorHandlingFilter" />
You might have to experiment with the order of the filter in the filter chain so that failed requests still go through the other filters you mentioned. If you're having trouble with that, a variation would be to do an HTTP redirect instead of a forward, like this:
try {
chain.doFilter(request, response);
} catch (NoHandlerFoundException e) {
request.getSession().setAttribute("exception", e);
response.sendRedirect("/servlet-exception.vm");
}
That would force the browser to request your error handling page as a new http request, which might make it easier to ensure it goes through all of the right filters first. If you need the original exception object, then you could put it in the session instead of the request.
maybe
<filter-mapping>
<filter-name>SomeFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>ERROR</dispatcher>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

Restart/ReInit a servlet

I want to restart a servlet (declared in web.xml, when JBoss is running) simply because its init-param points to a file which content has changed (i.e. providers.fac below has been modified).
If there is a way to reload the init-param without restarting the servlet, it will be good too.
I suppose I can modify the servlet to add a request param and function to restart itself ?
Is there any other option?
<servlet>
<servlet-name>coverage</servlet-name>
<servlet-class>coverageServlet</servlet-class>
<init-param>
<param-name>ConfigUrl</param-name>
<param-value>file:///C:/coverage/providers.fac</param-value>
</init-param>
<init-param>
<param-name>CacheDir</param-name>
<param-value>coverage</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Environment:
Servlet Api 2.4
JBoss 4.2
Spring Framework 2.5
If you are in jboss you can simply restart a servlet by altering the web.xml file if your servlet is exploded. On linux a touch would do.
Not sure what format your config file is but if you are trying to reload automatically a property configuration file I would have a look at the commons configuration lib that supports this out of the box(FileChangedReloadingStrategy)
If you are planning to restart your servlet automatically and many many times in a day/week you should make sure your permgen is good enough to handle the servlet reloads. There were instances where I had done this in production and burnt myself down with a lot of PermGen errors.
2 options:
Add an extra check on doGet() or doPost() which reloads the file when a certain request parameter is been set while an admin user is logged in and provide an admin screen which invokes that request.
Rewrite the servlet (or refactor the part to ServletContextListener) to store it in ServletContext instead of as an instance variable of the servlet and have some admin screen which reloads the file in ServletContext.
I would separate these concerns by pulling the file management out of the servlet and putting it into a JBoss JMX ServiceMBean. The MBean can take care of watching the file for changes and reloading when necessary, and can also expose the required operations [to the calling servlet]. This will allow you not to have to reload and re-init the servlet (or the WAR) which are fairly heavyweight operations.
I will invent a couple of operations for the FileManager:
public interface FileManagerMBean extends org.jboss.system.ServiceMBean {
public void setFileName(String fileName);
public void setCheckFrequency(long freq);
public String getCoverageData(......);
public String getProviderData(......);
}
The implementation might be (in the same package please :) )
public class FileManager extends org.jboss.system.ServiceMBeanSupport implements FileManagerMBean {
public void setFileName(String fileName) { .... }
public void setCheckFrequency(long freq) { .... }
public String getCoverageData(......) { /* impl */ }
public String getProviderData(......) { /* impl */ }
public void startService() throws Exception {
/* Start a file watcher thread */
}
public void stopService() throws Exception {
/* Stop the file watcher thread */
}
}
Your servlet might look like this:
// A ref to the MBean
FileManagerMBean fileMgr = null;
// The JMX MBean's ObjectName
ObjectName fileMgrOn = org.jboss.mx.util.ObjectNameFactory.create("portoalet.com:service=FileManager");
public void init() {
// Get the JBoss MBeanServer
MBeanServer server = org.jboss.mx.util.MBeanServerLocator.locateJBoss();
// Create an MBeanInvoker for the service
fileMgr = (FileManagerMBean)javax.management.MBeanServerInvocationHandler.newProxyInstance(server, fileMgrOn,FileManagerMBean.class, false);
}
Now you can use the fileMgr instance to make calls to your FileManager MBean, which should be thread safe unless you synchronize access to fileMgr.
I realize this looks a bit over-engineered, but you really should separate the functions of the servlet from the functions of managing the file.

Resources