Friday, 1 January 2016

Using interceptors in Spring MVC

Background

Interceptors as their name suggest intercepts request that are delegated to your controller by the dispacher setvlet. Why would we do that you ask? Well there are multiple possibilities. You can implement in these interceptors functionality that is common to multiple controllers. Like for eg - 
  • Add common model attributes
  • Set response header
  • Audit requests
  • Measure performance etc
In this post we will see how to implement those interceptors in Spring MVC.


HandlerInterceptor interface

HandlerIntercetor is an interface with following methods - 

  • boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler);
  • void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler. ModelAndView modelAndView);
  • void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler. Exception ex);
Their call sequence is reflected in following picture -




NOTE : 

preHandle() returns either true or false -
  • true : continue doewn the interceptor chain
  • false : invoke controller and skip remaining interceptors
Does this also mean you can have multiple interceptors? Precisely Yes! You can chain interceptors and they will be called one after another.

As in most case you don't want to implement all 3 methods so Spring has a separate class called - HandlerInterceptorAdapter which you can easily extend and override those methods that you need.


Configuring Interceptors

- Always keep in mind interceptors are are HandlerMapping level!

Spring as you know has multiple components like
  • HandlerMapping
  • HandlerAdapter
  • ViewResolver
  • HandlerExceptionResolver
 and user define components like
  • Controllers/Handler
  • Interceptors
There are 3 ways to configure interceptors

  1. Define interceptor as a property of your HandlerMapping bean -

    <bean class="...DefaultAnnotationHandlerMapping">
        <preoperty name="interceptors">
            <list>
                <bean class=""/>
                <bean class=""/>
            </list>
        </property>
    </bean>
  2. OR use mvc namespace to define interceptors -

    <mvc:interceptors>
        <bean class="myPackage.Interceptor1"/>
        <bean class="myPackage.Interceptor2"/>
    </mvc:interceptors>

    NOTE : This will be applied to all HandlerMapping beans. If you want to restrict your interceptors to particular HandlerMapping use -
  3. mapping paramter. You can optionally give exclude paramter too (available from Spring 3.1+)
    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/secure/*" />
            <mvc:exclude-mapping path="/secure/help" /> 
            <bean class="" />
        </mvc:interceptor>
    </mvc:interceptors>

Interceptor Example

Lets see an example of how to create an interceptor - 

Lets first create s simple servlet configuration to configure our dispacher Servlet - 

<?xml version="1.0" encoding="UTF-8"?>

<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

  <servlet>
    <servlet-name>admin</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <param-name>contextConfigLocation</param-name>
      <param-value>
                /WEB-INF/spring/*.xml
            </param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>admin</servlet-name>
    <url-pattern>/admin/*</url-pattern>
  </servlet-mapping>

</web-app>

As you can see our dispacher servlet will server all requests that are of format 
  • http://localhost:8080/projectName/admin
 projectName is the name of Spring project you have provided.


Now lets create the Spring configuration. Create a file under /WEB-INF/spring with xml extension and add following contents to it -

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/mvc 
                        http://www.springframework.org/schema/mvc/spring-mvc.xsd
                        http://www.springframework.org/schema/context 
                        http://www.springframework.org/schema/context/spring-context.xsd">


    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <context:component-scan base-package="myPackage" />

    <mvc:annotation-driven/>

     
    <mvc:interceptors>
            <bean class="myPackage.TestInterceptor" />
    </mvc:interceptors>     
    

</beans>


If you notice we have configured an interceptor to intercept all requests (i.e applicable for all handler mappings)

If you want it to be for specific URL you can do something like - 

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**" />
            <bean class="myPackage.TestInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors> 

or

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/home" />
            <bean class="myPackage.TestInterceptor" />
        </mvc:interceptor>
    </mvc:interceptors> 


Finally lets create our Controller and interceptor -

WelcomeController.java

@Controller

public class WelcomeController {
    @RequestMapping(value="/home", method = RequestMethod.GET)
    public String welcome() {
        return "welcome";
    }
}    

and TestInterceptor.java

public class TestInterceptor extends HandlerInterceptorAdapter {
    @Override
    public boolean preHandle(HttpServletRequest request,
            HttpServletResponse response, Object handler) throws Exception {
        System.out.println("Request intercepted");
        return true;
    }
    
}

After you have done this setup you can hit -
  • http://localhost:8080/projectName/admin/home
and see  "Request intercepted" printed in the logs. Also you can define multiple such <mvc:interceptor> tags thereby chaining them.

Also not interceptors will be hit only if the path that you are hitting is valid. Si if you try /home/test then interceptor will not be hit. It will be hit only if the requested path forms a part of valid HandlerMapping (like /home in above case).

Also will remind you again interceptors are configured at HandlerMapping level. In this case it would be RequestMappingHandlerMapping (Spring 3.1+ with mvc:annotation-driven) or DefaultAnnotationHandlerMapping.


Related Links

Difference between "/*" and "/**" in Spring MVC paths

Background

There are many places in Spring configuration where we need to provide path. It might be a mapping or a resource path. For example an interceptor mapping - 

<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/secure/**" />
        <mvc:exclude-mapping path="/secure/help" /> 
        <bean class="" />
    </mvc:interceptor>
</mvc:interceptors>

OR Servlet mapping in web.xml

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

or even your resource chain

<mvc:resources mapping="/css/**" location="/css/">
    <mvc:resource-chain resource-cache="true" auto-registration="true">
        <mvc:resolvers>
            <mvc:version-resolver>
                <mvc:content-version-strategy patterns="/**"/>
            </mvc:version-resolver>
        </mvc:resolvers>
    </mvc:resource-chain>
</mvc:resources>

In all these place you can use  "/*" or "/**". But the million dollar question is what does each mean and when do we use what?


Difference between "/*" and "/**" in Spring MVC paths

  • An asterisk ('*') matches zero or more characters, up to the occurrence of a '/' character (which serves as a path separator). A string, such as "/abcd/docs/index.html", would not match successfully against the pattern '/*/*.index.html'. The first asterisk matches up to the first path separator only, resulting in the "abcd" string. A successful matching pattern would be '/*/*/*.html'.
  • A string containing two asterisks ('**') matches zero or more characters. This could include the path separator '/'. In this case, "/abcd/docs/index.html" would successfully match the '/**/*.html' pattern. The double asterisk, including the path separator, would match the "abcd/docs" string.

So to sum up "/**" takes into account even the subpaths that may include the "/" i.e the path separator.

AntPathMatcher

This is a path pattern that used in Apache ant, spring team implement it and use it throughout the framework.

The mapping matches URLs using the following rules:

  • ? matches one character
  • * matches zero or more characters
  • ** matches zero or more 'directories' in a path
Some examples:

  • com/t?st.jsp - matches com/test.jsp but also com/tast.jsp or com/txst.jsp
  • com/*.jsp - matches all .jsp files in the com directory
  • com/**/test.jsp - matches all test.jsp files underneath the com path
  • org/springframework/**/*.jsp - matches all .jsp files underneath the org/springframework path
  • org/**/servlet/bla.jsp - matches org/springframework/servlet/bla.jsp but also org/springframework/testing/servlet/bla.jsp and org/servlet/bla.jsp

 Docs

Related Links

Saturday, 26 December 2015

Spring redirect using mvc:redirect-view-controller tag

Background

This is an extension of my previous post on 
So if you have directly landed here I would highly recommend to read it first for setup.  In this post we will essentially see how can we redirect one URL to other using mvc:redirect-view-controller tag configuration.


Spring redirect using mvc:redirect-view-controller tag

Basic setup remains the same as that of previous post

NOTE : For this Spring 4.1+ is required! So change your ivy dependency accordingly.

        <dependency org="org.springframework" name="spring-webmvc"
            rev="4.1.4.RELEASE">
            <exclude org="javax.servlet" name="javax.servlet-api" />
            <exclude org="javax.servlet.jsp" name="jsp-api" />
            <exclude org="javax.el" name="javax.el-api" />
        </dependency>

Now only change that you need to do in your spring configuration is as follows - 

    <mvc:redirect-view-controller redirect-url="/TestWebProject/newtest" path="/oldtest" status-code="308" keep-query-params="true"/>
    <mvc:view-controller path="/newtest" view-name="test"/>

All it says is if you get a URL "/oldtest" redirect it to new URL "/newtest" and the 2nd line states if you get url "/newtest" then render view with logical name test which will again render the same JSP test.jsp as per the InternalResourceViewResolver.






Notice the redirect with 308 response status.


Alternate way to redirect is using

<mvc:view-controller path="/oldtest" view-name="redirect:newtest"/>
 <mvc:view-controller path="/newtest" view-name="test"/>



Again notices the 302 response code.


NOTE : It is not desirable to use version in xsd declaration. Spring will use highest version in the project's dependencies if version not given.

How to install Maven on Windows

Prerequisites

  • We are going to install  Maven 3.3.9. So make sure you have JDK 7 installed. 
  • Set JAVA_HOME env variable to above JDK. For me it is C:\Program Files\Java\jdk1.7.0_79
  • Also I am using Windows 7.

How to install Maven on Windows

  • Download Maven zip file from their official site. We are going to install Maven 3.3.9. So go ahead download the corresponding zip file - apache-maven-3.3.9-bin.zip
  • Unzip it to some folder.  I have extracted it in folder - C:\Users\athakur\Softwares\apache-maven-3.3.9
  • Now add M2_HOME and MAVEN_HOME environment variable and point them to your extracted folder.
  • Append your %PATH% variable to add %M2_HOME%\bin
  • Make sure your JAVA_HOME is set to JDK 7 as per note below.
  • Finally you can execute mvn -version to see if maven is successful installed.







NOTE : Maven 3.3 requires JDK 1.7 or above to execute. Maven 3.2 requires JDK 1.6 or above, while Maven 3.0/3.1 requires JDK 1.5 or above

Related Links

Serving static resources with Spring MVC 3.0 in Java

Background

Many time we use custom CSS and java script file to add functionality and style to our web pages. We usually refer them in our JSP pages. In this post we will see how we can refer such static resources with Spring 3.0+. We will use Tomcat as our container/server.


Setup

If you are new to Spring then try out a sample Spring program -
Above post is somewhat old but should give you good idea as to get started. Anyhow I will provide the setup xmls here.

Your web.xml file should look as follows -

<web-app id="WebApp_ID" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

  <display-name>Spring Web MVC Demo Application</display-name>
   
   
  <servlet>
      <servlet-name>mvc-dispatcher</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/mvc-dispatcher-servlet.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
     <servlet-name>mvc-dispatcher</servlet-name>
        <url-pattern>/</url-pattern>
  </servlet-mapping>
  
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/root-context.xml</param-value>
    </context-param>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
   

</web-app>



 Important file here is mvc-dispatcher-servlet.xml which forms a part of web application context. Have given a dummy file root-context.xml for root application context but you may not add it in your web.xml file. If you are not familiar with context loading part I suggest read

As I said create file mvc-dispatcher-servlet.xml with following content -

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc 
                        http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/pages/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>
    
    <!-- Scan for components under this package -->
    <context:component-scan base-package="com.osfg.test" />



    <mvc:annotation-driven/>
    
</beans>


As you can see the web application context will scan package com.osfg.test to find Spring resources like handler/interceptor. GO ahead create this package and create a controller in it. Lets call it TestController.java. Add following content to it.

package com.osfg.test;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * @author athakur
 */
@Controller
public class TestController {
    
    @RequestMapping(value="/test", method=RequestMethod.GET)
    public String welcome() {
        return "test";
    }

}



That's it. We have our controller all setup. This controller returns a logical view name called test. Now if you notice our spring configuration we have already defined InternalResourceViewResolver which provides a JSTL view. So go ahead and create a JSP file at /pages/test.jsp under webapp or webcontent.

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>OSFG Test Page</title>
<link href="CSS/test.css" rel="stylesheet">
</head>
<body>

<h1>Hello World from OSFG!</h1>

</body>
</html>


As you can see our JSP references a CSS file called test.css. Create a folder called CSS and then create a file called test.cs  in it. Add following contents to it.
H1 {
color: white; background: teal; FONT-FAMILY: arial; FONT-SIZE: 18pt; FONT-STYLE: normal; FONT-VARIANT: normal
}



Ok we are all set to test now.

Note the Directory structure below -

 


Testing 1.0

Go ahead start up your tomcat server and load following URL
  • http://localhost:8080/TestWebProject/test 



 Oops! what happened? It is not able to find the CSS file.Well lets come to the point. Spring does not directly allow you to access static resources and the very goal of this post is to show you how. So lets see that now.

Add following to your Spring configuration -
<mvc:default-servlet-handler />


This basically allows you to render static resources from root folder (under webapp or webcontent).
It essentially configures a handler for serving static resources by forwarding to the Servlet container's default Servlet (DefaultServletHttpRequestHandler is used).
 Lets test with our new configuration -

Testing 2.0

After restarting the server hit the URL again -



All good? Yup :) There is also alternate way to do this. You can define resource mapping instead of specifying default-servlet-handler.

<mvc:resources mapping="/resources/**" location="/CSS/" />

Then you also need to make change in JSP as follows

<link href="resources/test.css" rel="stylesheet">

So when you reference resources it will go and pick up from CSS. You can use classpath references in location as well. Here ResourceHttpRequestHandler is used to serve request based on location provided.


Related Links





t> UA-39527780-1 back to top