Workshop 8
Workshop 8
import
org.springframework.web.servlet.support.AbstractAnnotationConfigDispatch
erServletInitializer;
@Override
protected Class<?>[] getRootConfigClasses() {
System.out.println("Inside Security Config Classes");
return new Class[] {MyConfiguration.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return null;
}
@Override
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return new String[] { "/" };
}
}
Note:
Above class will initialize the DispatcherServlet without web.xml file.
package ch8.mvc.configuration
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import
org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "ch8.mvc")
public class MyConfiguration extends WebMvcConfigurerAdapter {
@Bean(name="My")
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new
InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
System.out.println("Inside View Resolver");
return viewResolver;
}
}
Above class defines Spring MVC View Resolvers to avoid writing in dispatcher servlet-
servlet.xml file .Equivalent code for above code for web.xml is:
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver
">
<property name="prefix">
<value>/views/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
import
org.springframework.security.web.context.AbstractSecurityWebApplicationI
nitializer;
}
Note:
Above class is used to register the DelegatingFilterProxy to use the springSecurityFilterChain. It avoids writing
Filters configuration in web.xml file. The equivalent code that we keep in web.xml file for above is:
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
package ch8.mvc.configuration;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.config.annotation.authentication.builders.AuthenticationMana
gerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import
org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerA
dapter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
public void configureUsers(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("bill").password("abc123").roles("USER");
auth.inMemoryAuthentication().withUser("admin").password("root123").roles("ADMIN");
auth.inMemoryAuthentication().withUser("dba").password("root123").roles("ADMIN","DB
A");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and().formLogin().defaultSuccessUrl("/welcome")
.and().httpBasic();
}
}
Note:
9. Inside ch8.mvc.controller class, create a java class file namely MyController.java and
keep following lines of code inside it:
package ch8.mvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MyController {
@RequestMapping(value = { "/welcome","/"}, method =
RequestMethod.GET)
public String homePage(ModelMap model) {
model.addAttribute("greeting", "Hi, Welcome to mysite");
return "welcome";
}
}
10. Inside WeContent/WEB-INF folder, create folder named views and create jsp files
namely welcome.jsp
11. Put following lines of code inside welcome.jsp
<%@ page language="java" contentType="text/html; charset=ISO-
8859-1" pageEncoding="ISO-8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;
charset=ISO-8859-1">
<title>Welcome page</title>
</head>
<body>
Greeting :
This is a welcome page.
</body>
</html>
References:
1. http://www.journaldev.com/8718/spring-4-security-login-logout-inmemory-example
2. https://www.luckyryan.com/2013/02/07/migrate-spring-mvc-servlet-xml-to-java-config/
Note that in above code we have specified the URL for login page(/login) and if login is
successful, we have specified the URL of landing page URL(/welcome) and similarly if login is
not successful, we have specified the landing page URL(/Access_Denied)
package ch8.mvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class MyController {
return "accessDenied";
}
@RequestMapping(value = {"/login","/"}, method = RequestMethod.GET)
public String loginPage() {
return "login";
}
}
3. Inside views folder create three files namely login.jsp welcome.jsp and accessDenied.jsp
4. login.jsp should contain following codes:
<%@ page language="java" contentType="text/html;
charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c"
uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="form"
uri="http://www.springframework.org/tags/form" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Login Page</title>
</head>
<body>
<c:url var="loginUrl" value="/login" />
<form action="${loginUrl}" method="post">
<input type="hidden" name="${_csrf.parameterName}"
value="${_csrf.token}"/>
<c:if test="${param.error != null}">
<p>
Invalid username and password.
</p>
</c:if>
<p>
<label for="username">Username</label>
<input type="text" id="username" name="username"/>
</p>
<p>
<label for="password">Password</label>
<input type="password" id="password" name="password"/>
</p>
<button type="submit">Log in</button>
</form>
</body>
</html>
5. welcome.jsp should contain following codes
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Welcome page</title>
</head>
<body>
Greeting :
This is a welcome page.
</body>
</html>
Now, you have to specify the user name and password stored on mysql database
table[user1/pwd1 or user2/pwd2] in order to successfully logged onto the system.
In order for logout link to work, you have to follow below steps.
1. If login is successful, user sees welcome.jsp page. In welcome.jsp page, we will add
logout link as shown below:
2. In SecurityConfig.java, the configure() method should have following code:
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.antMatchers("/admin/**").access("hasRole('ADMIN')")
.antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')")
.and().formLogin().loginPage
("/login").defaultSuccessUrl("/welcome").failureUrl("/Access_Denied")
.usernameParameter("username").passwordParameter("password")
.and().logout().logoutRequestMatcher(new
AntPathRequestMatcher("/logout"))
.and().exceptionHandling().accessDeniedPage("/Access_Denied");
In above code, we have removed .and().csrf() because if we keep this code, then we have to send
logout request as POST request. In welcome.jsp, we are going to send /logout request as GET
request. In order to send logout request as a GET request, we have to specify logout() method as
shown above. It has to be noted that, above code alone is sufficient for logout to work. Whenever
we click, logout link on main.jsp, user is automatically redirected to login.jsp
.antMatchers("/admin").access("hasAuthority('ADMIN')")
It means access to URL /admin is permitted only to those users who has ADMIN role.
Hence, if you login with user1[which has ADMIN role], then only you are able to click
on "View Admin Page" link on main.jsp and then admin.jsp will be displayed. However,
of you were logged in using user2 then you are not able to display admin.jsp because
user2 does not have ADMIN role.
Above code is executed only if the currently logged user has either ADMIN or DBA role.
When asked for password, Enter "password" and fill the information accordingly and when
finished enter "yes" at last.
The JavaServer Pages Standard Tag Library (JSTL) is a collection of useful JSP tags which
encapsulates core functionality common to many JSP applications. JSTL has support for
common, structural tasks such as iteration and conditionals, tags for manipulating XML
documents, internationalization tags, and SQL tags.
So far we have learned securing our methods and login form. All the security implementations
were made at controller or model layer. We can also implement security at view layer. This is
necessary because we want to hide some content of a web page based on user's role so that he
will not be able to see those.
Spring provide basically 3 tags for securing view layer information i.e.
In above section "displaying page elements only to authenticated users in views" We have
already discussed about implementing above first two tags.
Following are the common expressions that can be used in access attribute of “<sec:authorize/>”
tag:
hasRole([role]) : Returns true only if the login user has the role specified in [role].
hasAnyRole([role1,role2]) : Returns true only if the login user has atleast one role
specified in [role1,role2]. The roles will be specified in comma separated format.
isAnonymous() : Returns true only is the login user is an anonymous user.
isAuthenticated() : Returns true if the user is not an anonymous user.
isFullyAuthenticated() : Returns true if the user is not an anonymous user or a
remember me user.
isRememberMe() : Returns true if the user is a remember me user.
The accesscontrollist tag is only valid when used with Spring Security's ACL module. It checks a
comma-separated list of required permissions for a specified domain object. If the current user
has any of those permissions, then the tag body will be evaluated. If they don't, it will be
skipped. An example might be
</sec:accesscontrollist>
Reference:
http://docs.spring.io/spring-security/site/docs/3.0.x/reference/taglibs.html
Spring Expression Language(SpEL) allows the developer to embed simple codes in the
configuration files much alike simple strings, which are executed runtime.
Even though, there are a lot of built-in expressions that we can use in Spring Security, such as
has Authority( ), hasAnyRole( ), permitAll( )etc, We might require some more expressions
which are not provided by Spring built in for example: "over18".
Now, we are going to create our own custom expression isLocal( ) which is true if user is
accessing the web application from local machine i.e. from the same machine where web server
resides.
package ch8.mvc.customexpression;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import
org.springframework.security.web.access.expression.WebSecurityExpressionRoot;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import
org.springframework.security.web.access.expression.WebSecurityExpressionRoot;
import org.springframework.security.access.expression.SecurityExpressionOperations;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.FilterInvocation;
import
org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHan
dler;
import
org.springframework.security.web.access.expression.WebSecurityExpressionRoot;
import org.springframework.stereotype.Component;
@Component
public class CustomWebSecurityExpressionHandler extends
DefaultWebSecurityExpressionHandler
{
private final AuthenticationTrustResolver trustResolver = new
AuthenticationTrustResolverImpl();
protected SecurityExpressionOperations
createSecurityExpressionRoot(Authentication authentication,FilterInvocation fi)
{
System.out.println("Inside the createSecurityExpressionRoot method");
WebSecurityExpressionRoot root = new
CustomWebSecurityExpressionRoot(authentication, fi);
root.setPermissionEvaluator(getPermissionEvaluator());
root.setTrustResolver(trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
public MyPermissionEvaluator getPermissionEvaluator()
{
return new MyPermissionEvaluator();
}
public RoleHierarchy getRoleHierarchy() {
RoleHierarchyImpl r = new RoleHierarchyImpl();
r.setHierarchy("USER > ADMIN ");
System.out.println("Role Hierarchy is Set");
return r;
}
}
Note:
i) In above code, We had set roleHierarchy and hence if any user who has USER
previleges, has also ADMIN privilege as defined inside getRoleHierarchy( )
method. Hence, if you login using user2/pwd2 and click "admin page" link on
main.jsp file, you are allowed to do it.
4. Create another class named MyPermissionEvaluator and put following codes inside it:
package ch8.mvc.customexpression;
import java.io.Serializable;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
@Override
public boolean hasPermission(Authentication authentication, Object
targetDomainObject, Object permission) {
boolean hasPermission = false;
if ( authentication != null && permission instanceof String){
hasPermission =false;
} else {
hasPermission =false;
}
System.out.println("Upper Method Called");
return hasPermission;
}
@Override
public boolean hasPermission(Authentication authentication,
Serializable targetId, String targetType, Object permission) {
System.out.println("Lower Method Called");
throw new RuntimeException("Id and Class permissions are not
supperted by this application");
}
}
5. Now, inside SecurityConfig.java, modify configure(HttpSecurity http) method as shown
below to register CustomWebSecurityExpressionHandler and use isLocal( ) expression as
shown below:
See the highlighted code above: It means access to /admin can be made if currently logged on
user had ADMIN role and the URL is being accessed from the local machine.
If we need to implement the complex rules, the intercept-url is unable to cope with the complex
rules. Because intercept-url is meant to secure at the URL-level only. However, The complex
rules are operating at the domain level. Hence, to implement complex rules; we have to use
ACL at the object level and intercept-url at the URL-level.
We are going to develop a bulleting application where user having above roles will manage
following three types of posts:
AdminPost
PersonalPost
PublicPost
[Quick Thought: We require a bulletin database along with three tables namely admin_post,
personal_post, public_post each table contain three fields namely id,date,message]
Implementation Steps:
In order to implement above requirement, we have to create two databases on mysql
namely: acl and bulletin. [ You can create these databases and following tables using
phpmyadmin]
In acl database, we have to create following four tables namely
acl_class,acl_sid,acl_object_identity and acl_entry
In bulleting database, We need to create three tables namely admin_post, personal_post
and public_post] Each table contains three columns namely id,date and message
acl_class is a table where we keep FQDN of domain object classes that we want to secure
eld Description
id The primary key
class The fully qualified name of the domain object
acl_sid is a table where we keep information about the user's or role that are going to
access on domain object.
Field Description
id The primary key
principal A flag to indicate if the sid field is a username or a role
sid The actual username (ie. john) or role (ie. ROLE_ADMIN)
Main complicated table is acl_object_identity. This table gathers information from three
tables[acl_class,acl_sid and all three tables of bulletin database] and provide this
information to acl_entry table. It keeps information like "Which particular particular row
of bulletin database table[object_id_identity] is associated with which particular domain
class[object_id_class]. We assign ADMIN user to each of the association as
well[owner_sid]. In conclusion, this table maintains information about domain objects by
relating actual rows of three database tables of bulletin database and acl_class[ There are
9 domain objects in our case since each table comprises three rows. Hence, 9 rows in this
table as well. ]
Field Description
Id The primary key
Refers to the id field in the acl_class. This is a reference to the fully qualified
object_id_class
name of the class
Refers to the primary id of the domain object. The id is assigned from another
object_id_identity database: the Bulletin database (See the Bulletin Database below). Every
domain object in the application needs to have a unique id.
parent_object Refers to the id of the parent object if existing
owner_sid Refers to the id field in the acl_sid. This is a reference to the username or role
entries_inheriting A flag to indicate whether the object has inherited entries
Main, important and core table is acl_entry. Acl_object_identity table stores information
about domain objects whereas this table stores which user has what permission on which
particular domain object. Domain object represented by acl_object_identity column that
references id field of acl_object_identity table. User is represented by sid and the
permission is represented by mask.
Field Description
id The primary key
acl_object_identity Refers to the id field in the acl_object_identity table
ace_order Refers to the ordering of the access control entries
sid Refers to the id field in the acl_sid table
A bitwise mask to indicate the permissions. A value of 1 is equivalent to READ
mask
permission, 2 for WRITE, and so forth.
A flag to indicate whether the mask should be interpreted as granting access or
granting
deny access
audit_success A flag to indicate whether to audit a successful permission
audit_failure A flag to indicate whether to audit a failed permission
Acl_entry table
Row no 1 to row no 15[ 15 rows] represent permission information for users having
ADMIN role[sid=1] i.e. mukesh user
o Row no 1 to 3 represent the fact that user having ADMIN role(sid=1 i.e. user
mukesh ) has read(mask=1) permission on all three rows of admin_post table
(acl_object_identity=1,2,3) [ Second Row Second Column]
o Row no 4 to 6 represent the fact that user having ADMIN role(sid=1 i.e. user
mukesh ) has write/delete/add(mask=2) permission on all three rows of
admin_post table (acl_object_identity=1,2,3) [ Second Row and Third,Fourth
and Fifth Column]
o Row no 7 to 9 represent the fact that user having ADMIN role(sid=1 i.e. user
mukesh ) has read(mask=1) permission on all three rows of personal_post
table (acl_object_identity=4,5,6)[ Third Row and Second Column]
o Row no 10 to 12 represent the fact that user having ADMIN role(sid=1 i.e.
user mukesh ) has read(mask=1) permission on all three rows of public_post
table (acl_object_identity=7,8,9) )[ Fourth Row and Second Column]
o Row no 13 to 15 represent the fact that user having ADMIN role(sid=1 i.e.
user mukesh ) has write/delete/add(mask=2) permission on all three rows of
public_post table (acl_object_identity=7,8,9) )[ Fourth Row and Third, Fourth
and Fifth Column]
o Row no 16 to 18 represent the fact that user having USER role(sid=2 i.e. user
manisha ) has read(mask=1) permission on all three rows of personal_post
table (acl_object_identity=4,5,6) )[ Third Row and Second Column]
o Row no 19 to 21 represent the fact that user having USER role(sid=2 i.e. user
manisha ) has add/edit/delete(mask=2) permission on all three rows of
personal_post table (acl_object_identity=4,5,6) )[ Third Row and
Third,Fourth and Fifth Column]
o Row no 22 to 24 represent the fact that user having USER role(sid=2 i.e. user
manisha ) has read(mask=1) permission on all three rows of public_post table
(acl_object_identity=7,8,9) )[ Fourth Row and Second Column]
o Row no 25 to 27 represent the fact that user having USER role(sid=2 i.e. user
manisha ) has add/edit/delete(mask=2) permission on all three rows of
personal_post table (acl_object_identity=7,8,9) )[ Fourth Row and
Third,Fourth and Fifth Column]
Row no 28 to row no 30 [ 3 rows] represent permission information for users having
VISITOR role[sid=1] i.e. muskan user.
In Matrix Form,
Role:Visitor
Post Type View Add Edit Delete
Admin
Personal
Public x
o Row no 28 to 30 represent the fact that user having Visitor role(sid=3 i.e. user
muskan ) has read(mask=1) permission on all three rows of public_post table
(acl_object_identity=7,8,9) )[ Fourth Row and Second Column]
Note:
Admin User read=1; Admin user Write=2;User read=3;User Write=4; Visitor Read=5
spring-security.xml
</security:http>
An authentication manager
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider>
<security:user-service>
<security:user name="mukesh"
password="21232f297a57a5a743894a0e4a801fc3" authorities="ROLE_ADMIN, ROLE_USER,
ROLE_VISITOR" />
<security:user name="manisha"
password="ee11cbb19052e40b07aac0ca060c23ee" authorities="ROLE_USER, ROLE_VISITOR" />
<security:user name="muskan"
password="127870930d65c57ee65fcc47f2170d38" authorities="ROLE_VISITOR" />
</security:user-service>
<security:password-encoder ref="passwordEncoder"/>
</security:authentication-provider>
</security:authentication-manager>
An Md5 password encoder
<bean
class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"
id="passwordEncoder"/>
acl-context.xml
<security:global-method-security pre-post-annotations="enabled">
<!-- Reference to a custom expression handler with ACL support -->
<security:expression-handler ref="expressionHandler" />
</security:global-method-security>
<bean id="expressionHandler"
class="org.springframework.security.access.expression.method.DefaultMethodSecu
rityExpressionHandler" p:permissionEvaluator-ref="permissionEvaluator"
p:roleHierarchy-ref="roleHierarchy" />
<bean class="org.springframework.security.acls.AclPermissionEvaluator"
id="permissionEvaluator">
<!-- Reference to the ACL service which performs JDBC calls to an ACL
database -->
<constructor-arg ref="aclService"/>
</bean>
4. An ACL service: We are going to use JdbcMutableAclService as an ACL service for our
application. The JdbcMutableAclService is a JDBC-based ACL service. It
uses JdbcTemplate to simplify JDBC access to MySQL ACL schema that we had created
earlier. By default the JdbcMutableAclService uses HSQLDB; even though PostgreSQL
schema is also available.
<bean class="org.springframework.security.acls.jdbc.JdbcMutableAclService"
id="aclService">
<constructor-arg ref="dataSource"/>
<constructor-arg ref="lookupStrategy"/>
<constructor-arg ref="aclCache"/>
</bean>
<bean id="lookupStrategy"
class="org.springframework.security.acls.jdbc.BasicLookupStrategy">
<constructor-arg ref="dataSource"/>
<constructor-arg ref="aclCache"/>
<constructor-arg ref="aclAuthorizationStrategy"/>
<constructor-arg ref="auditLogger"/>
</bean>
o The <constructor-arg ref="dataSource"/> is a reference to the same MySQL
datasource described below.
o The <constructor-arg ref="aclCache"/> is a reference to the same ACL cache
described below.
o The <constructor-arg ref="aclAuthorizationStrategy"/> is a reference to
an AclAuthorizationStrategyimplementation.
6. A datasource: This is a standard MySQL datasource that uses a C3P0 connection pool.
The jdbcUrl property points to the acl database.
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="com.mysql.jdbc.Driver"
p:jdbcUrl="jdbc:mysql://localhost:3306/acl"
p:user="root"
p:password="opmcm6718"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="100"
p:maxStatements="50"
p:minPoolSize="10" />
7. An ACL cache : We declare Spring Security's default implementation
of AclCache interface in order to lessen database lookups. Ehcache is an open source,
standards-based cache used to boost performance, offload the database and simplify
scalability. The EhCacheBasedAclCache constructor accepts an instance of an EHCache
instance. TheEhCacheFactoryBean is responsible for crafting this instance.
8. An ACL authorization strategy: It is used to determine whether a principal is permitted to
call administrative methods.
<bean id="aclAuthorizationStrategy"
class="org.springframework.security.acls.domain.AclAuthorizationStrategy
Impl">
<constructor-arg>
<list>
<bean
class="org.springframework.security.core.authority.SimpleGrantedAuthorit
y">
<constructor-arg value="ROLE_ADMIN"/>
</bean>
<bean
class="org.springframework.security.core.authority.SimpleGrantedAuthorit
y">
<constructor-arg value="ROLE_ADMIN"/>
</bean>
<bean
class="org.springframework.security.core.authority.SimpleGrantedAuthorit
y">
<constructor-arg value="ROLE_ADMIN"/>
</bean>
</list>
</constructor-arg>
</bean>
9. A role hierarchy: Role hierarchy is a way of declaring which role is the boss of other
roles. In our sample configuration ROLE_ADMIN > ROLE USER, means whenever a
user has a ROLE_ADMIN, he also gets
the ROLE_USER. And because we declared ROLE_USER > ROLE_VISITOR, he also
gets the ROLE_VISITOR.
<bean id="roleHierarchy"
class="org.springframework.security.access.hierarchicalroles.RoleHierarchyImp
">
<property name="hierarchy">
<value>
ROLE_ADMIN > ROLE_USER
ROLE_USER > ROLE_VISITOR
</value>
</property>
</bean>
Steps for creating application:
1. Create a Dynamic Web Project named SpringACL and include following 27 different jar
files both in Java Build Path and Deployment Assembly
c3po-0.9.0.4 cglib-nodep-2.2 aopalliance-1.0.0 commons-logging- ehcache-2.7.0
1.2
hsqldb-2.2.8 jstl-1.2 log4j-1.2.15 mysqlconnector sl4j-api-1.7.21.jar
sl4j-log4j12-1.7.21 spring-aop- spring-beans- spring-context- spring-context-
4.2.2.RELEASE.jar 4.2.2.RELEASE.jar 4.2.2.RELEASE.jar support-
4.2.2.RELEASE.jar
spring-core- spring-expression- spring-jdbc- spring-security-acl- spring-security-
4.2.2.RELEASE.jar 4.2.2.RELEASE.jar 3.2.8.RELEASE.jar 3.1.2.RELEASE.jar config-
4.1.0.RELEASE.jar
spring-security-core- spring-security-web- spring-tx- spring-web- spring-webmvc-
4.1.0.RELEASE.jar 4.1.0.RELEASE.jar 4.2.2.RELEASE.jar 4.2.2.RELEASE.jar 4.2.2.RELEASE.jar
standard-1.1.2.jar taglibs-standard-
impl-1.2.5.jar
import java.util.Date;
/**
* A simple POJO representing admin posts
*/
public class AdminPost implements Post {
private Long id;
private Date date;
private String message;
import java.util.Date;
/**
* A simple POJO representing personal posts
*/
public class PersonalPost implements Post {
private Long id;
private Date date;
private String message;
import java.util.Date;
/**
* A simple POJO representing public posts
*/
public class PublicPost implements Post {
private Long id;
private Date date;
private String message;
/**
* A simple interface for Post objects
*/
public interface Post {
public Long getId();
package ch8.acl.demo.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ch8.acl.demo.domain.AdminPost;
import ch8.acl.demo.domain.Post;
/**
* Service for processing Admin related posts.
* <p>
* For a complete reference to Spring JDBC and JdbcTemplate
* see http://static.springsource.org/spring/docs/3.0.x/spring-framework-
reference/html/jdbc.html
* <p>
* For transactions, see http://static.springsource.org/spring/docs/3.0.x/spring-
framework-reference/html/transaction.html
*/
@SuppressWarnings("deprecation")
@Service("adminService")
@Transactional
public class AdminService implements GenericService {
@Resource(name="bulletinDataSource")
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
@SuppressWarnings("deprecation")
public Post getSingle(Long id) {
try {
logger.debug("Retrieving single admin post");
} catch (Exception e) {
logger.error(e);
return null;
}
}
} catch (Exception e) {
logger.error(e);
return null;
}
}
// Save
jdbcTemplate.update(sql, parameters);
// Return
return true;
} catch (Exception e) {
logger.error(e);
return false;
}
}
// Return
return true;
} catch (Exception e) {
logger.error(e);
return false;
}
}
// Delete
jdbcTemplate.update(sql, parameters);
// Return
return true;
} catch (Exception e) {
logger.error(e);
return false;
}
}
import org.apache.log4j.Logger;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ch8.acl.demo.domain.PersonalPost;
import ch8.acl.demo.domain.Post;
/**
* Service for processing Personal related posts.
* <p>
* For a complete reference to Spring JDBC and JdbcTemplate
* see http://static.springsource.org/spring/docs/3.0.x/spring-framework-
reference/html/jdbc.html
* <p>
* For transactions, see http://static.springsource.org/spring/docs/3.0.x/spring-
framework-reference/html/transaction.html
*/
@Service("personalService")
@Transactional
public class PersonalService implements GenericService {
@Resource(name="bulletinDataSource")
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
public Post getSingle(Long id) {
try {
logger.debug("Retrieving single personal post");
} catch (Exception e) {
logger.error(e);
return null;
}
}
// Save
jdbcTemplate.update(sql, parameters);
// Return
return true;
} catch (Exception e) {
logger.error(e);
return false;
}
}
public Boolean edit(Post post) {
try {
logger.debug("Adding new post");
// Save
jdbcTemplate.update(sql, parameters);
// Return
return true;
} catch (Exception e) {
logger.error(e);
return false;
}
}
// Delete
jdbcTemplate.update(sql, parameters);
// Return
return true;
} catch (Exception e) {
logger.error(e);
return false;
}
}
}
o PublicService class comprises of methods to add/edit/delete posts, retrieve all
posts, get a particular post by user having ROLE_VISITOR authority.
package ch8.acl.demo.service;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ch8.acl.demo.domain.Post;
import ch8.acl.demo.domain.PublicPost;
/**
* Service for processing Public related posts.
* <p>
* For a complete reference to Spring JDBC and JdbcTemplate
* see http://static.springsource.org/spring/docs/3.0.x/spring-framework-
reference/html/jdbc.html
* <p>
* For transactions, see http://static.springsource.org/spring/docs/3.0.x/spring-
framework-reference/html/transaction.html
*/
@Service("publicService")
@Transactional
public class PublicService implements GenericService {
@Resource(name="bulletinDataSource")
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new SimpleJdbcTemplate(dataSource);
}
} catch (Exception e) {
logger.error(e);
return null;
}
}
// Save
jdbcTemplate.update(sql, parameters);
// Return
return true;
} catch (Exception e) {
logger.error(e);
return false;
}
}
// Save
jdbcTemplate.update(sql, parameters);
// Return
return true;
} catch (Exception e) {
logger.error(e);
return false;
}
}
// Delete
jdbcTemplate.update(sql, parameters);
// Return
return true;
} catch (Exception e) {
logger.error(e);
return false;
}
}
}
o GenericService is an interface implemented by AdminService, PersonalService
and PublicService class.
package ch8.acl.demo.service;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.security.access.prepost.PreAuthorize;
import ch8.acl.demo.domain.Post;
/**
* A generic service for handling CRUD operations.
* <p>
* The method access-control expressions are specified in this interface.
*/
public interface GenericService {
/**
* Inject the datasource for the bulletingapplication
*/
public void setDataSource(DataSource dataSource);
/**
* Retrieves a single post.
* <p>
* Access-control will be evaluated after this method is invoked.
* returnObject refers to the returned object.
*/
@PostAuthorize("hasPermission(returnObject, 'WRITE')")
public Post getSingle(Long id);
/**
* Retrieves all posts.
* <p>
* Access-control will be evaluated after this method is invoked.
* filterObject refers to the returned object list.
*/
@PostFilter("hasPermission(filterObject, 'READ')")
public List<Post> getAll();
/**
* Adds a new post.
* <p>
* We don't provide any access control here because
* the new object doesn't have an id yet.
* <p>
* Instead we place the access control on the URL-level because
* the Add page shouldn't be visible in the first place.
* <p>
* There are two places where we can place this restriction:
* <pre>
* 1. At the controller method
* 2. At the external spring-security.xml file</pre>
* <p>
*
*/
public Boolean add(Post post);
/**
* Edits a post.
* <p>
* Access-control will be evaluated before this method is invoked.
* <b>#post</b> refers to the current object in the method argument.
*/
@PreAuthorize("hasPermission(#post, 'WRITE')")
public Boolean edit(Post post);
/**
* Deletes a post.
* <p>
* Access-control will be evaluated before this method is invoked.
* <b>#post</b> refers to the current object in the method argument.
*/
@PreAuthorize("hasPermission(#post, 'WRITE')")
public Boolean delete(Post post);
}
5. Inside ch8.acl.demo.controller create following 8 different controller files namely
AdminController, BulletinController, LoginLogout Controller, PersonalController,
PublicController and RoleController.
o LoginLogoutController: This class is responsible for processing a request to
display login page.
package ch8.acl.demo.controller;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
/**
* Retrieves login related pages
*/
@Controller
@RequestMapping("/auth")
public class LoginLogoutController {
/**
* Retrieves the login page
*/
@RequestMapping(value="/main", method = RequestMethod.GET)
public String printWelcome( ) {
return "main_page";
}
@RequestMapping(value = "/login", method = RequestMethod.GET)
public String getLoginPage(@RequestParam(value="error",
required=false) boolean error,
ModelMap model) {
/**
* Retrieves the denied page
*/
@RequestMapping(value = "/denied", method = RequestMethod.GET)
public String getDeniedPage() {
logger.debug("Received request to show denied page");
import javax.annotation.Resource;
import org.apache.log4j.Logger;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import ch8.acl.demo.service.GenericService;
import ch8.acl.demo.service.PersonalService;
import ch8.acl.demo.service.PublicService;
/**
* Handles Bulletin related requests
*/
@Controller
@RequestMapping("/bulletin")
public class BulletinController{
@Resource(name="adminService")
private GenericService adminService;
@Resource(name="personalService")
private GenericService personalService;
@Resource(name="publicService")
private GenericService publicService;
/**
* Retrieves the View page.
* <p>
* This loads all authorized posts.
*/
@RequestMapping(value = "/view", method = RequestMethod.GET)
public String getViewAllPage(Model model) {
logger.debug("Received request to view all page");
System.out.println("Inside Bulleting View
Pages="+adminService.getAll().size());
// Retrieve items from service and add to model
model.addAttribute("adminposts", adminService.getAll());
model.addAttribute("personalposts", personalService.getAll());
model.addAttribute("publicposts", publicService.getAll());
package ch8.acl.demo.controller;
import java.util.Date;
import org.apache.log4j.Logger;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import ch8.acl.demo.domain.AdminPost;
import ch8.acl.demo.domain.Post;
import ch8.acl.demo.service.GenericService;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import javax.annotation.Resource;
/**
* Handles Admin-related requests
*/
@Controller
@RequestMapping("/admin")
public class AdminController {
@Resource(name="adminService")
private GenericService adminService;
/**
* Retrieves the Edit page
*/
@RequestMapping(value = "/edit", method = RequestMethod.GET)
public String getEdit(@RequestParam(value="id", required=true) Long id,
Model
model) {
logger.debug("Received request to show edit page");
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Admin");
// This will resolve to /WEB-INF/jsp/crud-admin/editpage.jsp
return "crud-admin/editpage";
}
/**
* Saves the edited post from the Edit page and returns a result page.
*/
@RequestMapping(value = "/edit", method = RequestMethod.POST)
public String getEditPage(@ModelAttribute("postAttribute") AdminPost post,
// Re-assign id
post.setId(id);
// Assign new date
post.setDate(new Date());
// Delegate to service
if (adminService.edit(post) == true) {
// Add result to model
model.addAttribute("result", "Entry has been edited successfully!");
} else {
// Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Admin");
/**
* Retrieves the Add page
* <p>
* Access-control is placed here (instead in the service) because we don't want
* to show this page if the client is unauthorized and because the new
* object doesn't have an id. The hasPermission requires an existing id!
*/
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
@RequestMapping(value = "/add", method = RequestMethod.GET)
public String getAdd(Model model) {
logger.debug("Received request to show add page");
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Admin");
/**
* Saves a new post from the Add page and returns a result page.
*/
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String getAddPage(@ModelAttribute("postAttribute") AdminPost post,
Model model) {
logger.debug("Received request to view add page");
// Delegate to service
if (adminService.add(post)) {
// Success. Add result to model
model.addAttribute("result", "Entry has been added successfully!");
} else {
// Failure. Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Admin");
/**
* Deletes an existing post and returns a result page.
*/
@RequestMapping(value = "/delete", method = RequestMethod.GET)
public String getDeletePage(@RequestParam(value="id", required=true) Long
id,
Model
model) {
logger.debug("Received request to view delete page");
// Delegate to service
if (adminService.delete(post)) {
// Add result to model
model.addAttribute("result", "Entry has been deleted successfully!");
} else {
// Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Admin");
package ch8.acl.demo.controller;
import java.util.Date;
import org.apache.log4j.Logger;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import ch8.acl.demo.domain.PersonalPost;
import ch8.acl.demo.domain.Post;
import ch8.acl.demo.service.GenericService;
import javax.annotation.Resource;
/**
* Handles Personal-related requests
*/
@Controller
@RequestMapping("/personal")
public class PersonalController {
@Resource(name="personalService")
private GenericService personalService;
/**
* Retrieves the Edit page
*/
@RequestMapping(value = "/edit", method = RequestMethod.GET)
public String getEdit(@RequestParam(value="id", required=true) Long id,
Model
model) {
logger.debug("Received request to show edit page");
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Personal");
/**
* Saves the edited post from the Edit page and returns a result page.
*/
@RequestMapping(value = "/edit", method = RequestMethod.POST)
public String getEditPage(@ModelAttribute("postAttribute") PersonalPost
post,
@RequestParam(value="id", required=true) Long id,
Model model)
{
logger.debug("Received request to view edit page");
// Re-assign id
post.setId(id);
// Assign new date
post.setDate(new Date());
// Delegate to service
if (personalService.edit(post) == true) {
// Add result to model
model.addAttribute("result", "Entry has been edited successfully!");
} else {
// Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Personal");
/**
* Retrieves the Add page
* <p>
* Access-control is placed here (instead in the service) because we don't want
* to show this page if the client is unauthorized and because the new
* object doesn't have an id. The hasPermission requires an existing id!
*/
@PreAuthorize("hasAuthority('ROLE_USER') and !
hasAuthority('ROLE_ADMIN')")
@RequestMapping(value = "/add", method = RequestMethod.GET)
public String getAdd(Model model) {
logger.debug("Received request to show add page");
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Personal");
/**
* Saves a new post from the Add page and returns a result page.
*/
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String getAddPage(@ModelAttribute("postAttribute") PersonalPost
post, Model model) {
logger.debug("Received request to view add page");
// Delegate to service
if (personalService.add(post)) {
// Success. Add result to model
model.addAttribute("result", "Entry has been added successfully!");
} else {
// Failure. Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Personal");
/**
* Deletes an existing post and returns a result page.
*/
@RequestMapping(value = "/delete", method = RequestMethod.GET)
public String getDeletePage(@RequestParam(value="id", required=true) Long
id,
Model
model) {
logger.debug("Received request to view delete page");
// Delegate to service
if (personalService.delete(post)) {
// Add result to model
model.addAttribute("result", "Entry has been deleted successfully!");
} else {
// Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Personal");
o PublicController: This class is responsible for displaying edit record form, delete
record form,processing add,edit and delte post request and forwarding it to
AdminService class for those users who has ROLE_VISITOR authority.
package ch8.acl.demo.controller;
import java.util.Date;
import org.apache.log4j.Logger;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import ch8.acl.demo.domain.Post;
import ch8.acl.demo.domain.PublicPost;
import ch8.acl.demo.service.GenericService;
import javax.annotation.Resource;
/**
* Handles Public-related requests
*/
@Controller
@RequestMapping("/public")
public class PublicController {
@Resource(name="publicService")
private GenericService publicService;
/**
* Retrieves the Edit page
*/
@RequestMapping(value = "/edit", method = RequestMethod.GET)
public String getEdit(@RequestParam(value="id", required=true) Long id,
Model
model) {
logger.debug("Received request to show edit page");
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Public");
/**
* Saves the edited post from the Edit page and returns a result page.
*/
@RequestMapping(value = "/edit", method = RequestMethod.POST)
public String getEditPage(@ModelAttribute("postAttribute") PublicPost post,
// Re-assign id
post.setId(id);
// Assign new date
post.setDate(new Date());
// Delegate to service
if (publicService.edit(post) == true) {
// Add result to model
model.addAttribute("result", "Entry has been edited successfully!");
} else {
// Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Public");
/**
* Retrieves the Add page
* <p>
* Access-control is placed here (instead in the service) because we don't want
* to show this page if the client is unauthorized and because the new
* object doesn't have an id. The hasPermission requires an existing id!
*/
@PreAuthorize("hasAuthority('ROLE_USER') or
hasAuthority('ROLE_ADMIN')")
@RequestMapping(value = "/add", method = RequestMethod.GET)
public String getAdd(Model model) {
logger.debug("Received request to show add page");
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Public");
/**
* Saves a new post from the Add page and returns a result page.
*/
@RequestMapping(value = "/add", method = RequestMethod.POST)
public String getAddPage(@ModelAttribute("postAttribute") PublicPost post,
Model model) {
logger.debug("Received request to view add page");
// Delegate to service
if (publicService.add(post)) {
// Success. Add result to model
model.addAttribute("result", "Entry has been added successfully!");
} else {
// Failure. Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Public");
/**
* Deletes an existing post and returns a result page.
*/
@RequestMapping(value = "/delete", method = RequestMethod.GET)
public String getDeletePage(@RequestParam(value="id", required=true) Long
id,
Model
model) {
logger.debug("Received request to view delete page");
// Delegate to service
if (publicService.delete(post)) {
// Add result to model
model.addAttribute("result", "Entry has been deleted successfully!");
} else {
// Add result to model
model.addAttribute("result", "You're not allowed to perform that action!");
}
// Add source to model to help us determine the source of the JSP page
model.addAttribute("source", "Public");
package ch8.acl.demo.controller;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* Retrieves welcome pages for various roles.
* These pages are secured via intercept-urls. See the security-context.xml file
*/
@Controller
@RequestMapping("/role")
public class RoleController {
/**
* Retrieves the admin page.
* <p>
* Only clients with ROLE_ADMIN can see this page
*/
@RequestMapping(value = "/admin", method = RequestMethod.GET)
public String getAdminPage() {
logger.debug("Received request to show admin page");
/**
* Retrieves the user page.
* <p>
* Only clients with ROLE_ADMIN and ROLE_USER can see this page
*/
@RequestMapping(value = "/user", method = RequestMethod.GET)
public String getUserPage() {
logger.debug("Received request to show user page");
/**
* Retrieves the visitor page.
* <p>
* Only clients with ROLE_ADMIN, ROLE_USER, and ROLE_VISITOR can
see this page
*/
@RequestMapping(value = "/visitor", method = RequestMethod.GET)
public String getVisitorPage() {
logger.debug("Received request to show visitor page");
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring-security.xml
/WEB-INF/applicationContext.xml
</param-value>
</context-param>
<servlet>
<description></description>
<display-name>spring</display-name>
<servlet-name>spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</
servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/spring</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>spring</servlet-name>
<url-pattern>/acldemo/*</url-pattern>
</servlet-mapping>
<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-
class>
</listener>
</web-app>
o Inside spring-servlet.xml, keep following codes:
</beans>
</security:http>
</beans:beans>
http://www.springframework.org/schema/security/spring-security-
4.1.xsd
http://www.springframework.org/schema/jdbc
http://www.springframework.org/schema/jdbc/spring-
jdbc.xsd">
<!-- A MySQL datasource with pooling capabalities for the ACL module
-->
<bean id="dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close"
p:driverClass="com.mysql.jdbc.Driver"
p:jdbcUrl="jdbc:mysql://localhost:3306/acl"
p:user="root"
p:password="opmcm6718"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="100"
p:maxStatements="50"
p:minPoolSize="10" />
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-
mvc.xsd">
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-
beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-
tx.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
<!-- Enable annotation style of managing transactions -->
p:driverClass="com.mysql.jdbc.Driver"
p:jdbcUrl="jdbc:mysql://localhost:3306/bulletin"
p:user="root"
p:password="opmcm6718"
p:acquireIncrement="5"
p:idleConnectionTestPeriod="60"
p:maxPoolSize="100"
p:maxStatements="50"
p:minPoolSize="10" /
</beans>
7. Inside WebContent Folder create a folder namely jsp and place 3 jsp files namely
loginpage.jsp, bulletinpage.jsp and deniedpage.jsp over there.
o loginpage.jsp page is contains source code for displaying login dialog box
<table>
<tr>
<td>User:</td>
<td><input type='text' id= 'username' name='username' value=''>
</td>
</tr>
<tr>
<td>Password:</td>
<td><input type='password' id= 'password' name='password' />
</td>
</tr>
<tr>
<td colspan='2'><input name="submit" type="submit"
value="submit" />
</td>
<td colspan='2'><input name="reset" type="reset" />
</td>
</tr>
</table>
</form>
</body>
</html>
o bulletinpage.jsp contains source code for displaying a screen once user
successfully logs onto the system
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Bulletin Page</h1>
<p>
<span style="font-weight:bold">Current user: </span>${username}<br/>
<span style="font-weight:bold">Current role: </span>${role}
</p>
<hr/>
</body>
</html>
o deniedpage.jsp contains source code for displaying a screen if the username or
password is in-correct.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Access Denied!</h1>
<p>You are not allowed to access this resource!</p>
</body>
</html>
8. Inside jsp folder create four folder namely crud-admin, crud-personal,crud-public and
role
9. Inside crud-admin folder create three jsp files namely addpage.jsp, editpage.jsp and
resultpage.jsp
o addpage.jsp: Represents code for displaying add posts view by user having
ROLE_ADMIN authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"
%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
</body>
</html>
o editpage.jsp: Represents code for displaying editing posts view by user having
ROLE_ADMIN authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"
%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<tr>
<td><form:label path="date">Date:</form:label></td>
<td><form:input path="date" disabled="true"/></td>
</tr>
<tr>
<td><form:label
path="message">Message:</form:label></td>
<td><form:input path="message"/></td>
</tr>
</table>
</body>
</html>
o resultpage.jsp: Displays add/edit posts operation status[Successful/Unsuccessful]
by user having ROLE_ADMIN authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Bulletin Page</h1>
<p>
<span style="font-weight:bold">Current user: </span>${username}<br/>
<span style="font-weight:bold">Current role: </span>${role}
</p>
<hr/>
<p>
${source} <br/><br/>
${result}
</p>
</body>
</html>
10. Inside crud-personal folder create three jsp files namely addpage.jsp, editpage.jsp and
resultpage.jsp
a. addpage.jsp: Represents code for displaying add posts view by user having
ROLE_USER authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"
%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
</body>
</html>
b. editpage.jsp: Represents code for displaying editing posts view by user having
ROLE_USER authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"
%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<tr>
<td><form:label path="date">Date:</form:label></td>
<td><form:input path="date" disabled="true"/></td>
</tr>
<tr>
<td><form:label
path="message">Message:</form:label></td>
<td><form:input path="message"/></td>
</tr>
</table>
</body>
</html>
c. resultpage.jsp: Displays add/edit posts operation status [Successful/Unsuccessful]
by user having ROLE_USER authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Bulletin Page</h1>
<p>
<span style="font-weight:bold">Current user: </span>${username}<br/>
<span style="font-weight:bold">Current role: </span>${role}
</p>
<hr/>
<p>
${source} <br/><br/>
${result}
</p>
</body>
</html>
11. Inside crud-public folder create three jsp files namely addpage.jsp, editpage.jsp and
resultpage.jsp
a. addpage.jsp: Represents code for displaying add posts view by user having
ROLE_VISITOR authority.
<%@ 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>Insert title here</title>
</head>
<body>
</body>
</html>
b. editpage.jsp: Represents code for displaying editing posts view by user having
ROLE_VISITOR authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"
%>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<tr>
<td><form:label path="date">Date:</form:label></td>
<td><form:input path="date" disabled="true"/></td>
</tr>
<tr>
<td><form:label
path="message">Message:</form:label></td>
<td><form:input path="message"/></td>
</tr>
</table>
</body>
</html>
c. resultpage.jsp: Displays add/edit posts operation status[Successful/Unsuccessful]
by user having ROLE_VISITOR authority.
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!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=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>Bulletin Page</h1>
<p>
<span style="font-weight:bold">Current user: </span>${username}<br/>
<span style="font-weight:bold">Current role: </span>${role}
</p>
<hr/>
<p>
${source} <br/><br/>
${result}
</p>
</body>
</html>
12. Inside role folder create three jsp files namely adminpage.jsp,userpage.jsp and
visitorpage.jsp [ These are just the test files only ]
Executing:
http://localhost:8080/SpringACL/acldemo/auth/login
13.
Reference:
http://krams915.blogspot.in/2011/01/spring-security-3-full-acl-tutorial.html
Acegi is a powerful security framework used especially for securing Java Enterprise Application
built using Spring Framework. Spring-based dependency injection makes Acegi easy to
configure and implement.
Acegi provides authentication service through the use of a web filter, an AuthenticationProvider
and an AuthenticationEntryPoint. Authorization in Acegi can be done at different
levels:authorizing web requests, authorizing which methods can be invoked, and authorizing
access to individual domain object instances.
References:
1. http://java-x.blogspot.in/2006/12/spring-security-with-acegi-security.html
2. http://www.javaworld.com/article/2077780/java-web-development/acegi-security-in-one-
hour.html
Display data coming from the authentication layer, for example the user's name and roles,
or hide and display part of a web page according to user's authorities.
The thymeleaf-extras-springsecurity module allows us to do so.
Example:
Scheduling
Not all applications are driven by user input. Some applications need to execute without any user
intervention probably running at the background at specified intervals. For instance, a Virus
Scanner application will be running in the background once in 2 days. Another instance could be
where a Software could connect to its server repository once in a day for any updates.
Spring provides integration support for JDK Timer and Quartzthat facilitates in writing job
scheduling applications
Quartz Scheduler
Quartz scheduler helps scheduler to run job or task at a specified date and time. Quartz is a Java
open source job-scheduling system capable of scheduling and executing jobs. A job is a Java
class containing the task to be executed. Job is scheduled by trigger which defines when to
execute a Job. Quartz trigger responsibility is to determine when to run Quartz’s job
1. Create a Dynamic Web Project named SchedulerDemo and include following 12 jar files
both in Java Build Path and Deployment Assembly.
commons-logging-1.2.jar,log4j-1.2.15.jar,quartz-1.8.6.jar,slf4j-api-1.7.21.jar,
slf4j-log4j12-1.7.21.jar,spring-beans-4.2.2.RELEASE.jar,spring-context-
4.2.2.RELEASE.jar,spring-context-support-3.0.5.RELEASE.jar,spring-core-
4.2.2.RELEASE.jar,spring-expression-4.2.2.RELEASE.jar,spring-tx-
4.2.2.RELEASE.jar,spring-web-4.2.2.RELEASE.jar
2. Creating two packages namely scheduling.jobs and scheduling.tasks
3. Create files namely AnotherTask.java and SomeTask.java inside scheduling tasks
package. These two files represent tasks. Codes inside the method of these classes will be
executed by Quartz Scheduler in fixed interval of time.
Put Following Codes inside SomeTask.java
package scheduling.tasks;
public class SomeTask {
}
Put Following Codes inside AnotherTask.java
package scheduling.tasks;
4. Create a java file named SampleQuartzJob.java inside scheduling.tasks package and put
following codes inside it.
package scheduling.jobs;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.springframework.scheduling.quartz.QuartzJobBean;
import scheduling.tasks.AnotherTask;
import scheduling.tasks.SomeTask;
@Override
protected void executeInternal(JobExecutionContext
jobExecutionContext)
throws JobExecutionException {
someTask.doSomething();
anotherTask.doSomethingElse();
}
5. Create index.jsp file inside WebContent directory and put following codes inside it:
<html>
<body>
<h2>Hello Friends!</h2>
<h3>Check out the console for sysouts from the scheduled jobs.</h3>
</body>
</html>
<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
<bean name="sampleQuartzJobBean"
class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobDataAsMap">
<map>
<entry key="someTask" value-ref="someTask" />
<entry key="anotherTask" value-ref="anotherTask" />
</map>
</property>
</bean>
<bean id="sampleQuartzJobCronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="sampleQuartzJobBean" />
<property name="cronExpression" value="0/15 * * * * ?" />
</bean>
<bean
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
<ref bean="sampleQuartzJobBean" />
</list>
</property>
<property name="triggers">
<list>
<ref bean="sampleQuartzJobCronTrigger" />
</list>
</property>
</bean>
</beans>
8. Right Click your project-> Click Run As-> Run On Server to execute your project. You
can see output of your application on the spring console.
Main part of the code for above application we have kept inside applicationContext.xml. Let us
analyze the code of this file.
Components of Quartz:
Job
In our example above, SomeTask class has method named doSomething() and AnotherTask has
method named doSomethingElse( ). Indeed, the codes that we write inside these methods
represent job i.e. task to be executed in certain interval of time.
JobDetail
1. JobDetail Bean: To implement this bean, you have to follow below steps;
i) We have to create a class called SampleQuartzJob inside scheduling.jobs package
and set it as property "jobClass" of JobDetailBean class.
ii) Similalry, We have to set above two jobs as "jobDataAsMap" property
Example:
<bean name="sampleQuartzJobBean"
class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobDataAsMap">
<map>
<entry key="someTask" value-ref="someTask" />
<entry key="anotherTask" value-ref="anotherTask" />
</map>
</property>
</bean>
iii) In SampleQuartzJob class, we should have setter methods for setting job objects
and executeInternal(JobExecutionContext jobExecutionContext) method where
we specify codes that the scheduler wants to repeatedly execute:
Example:
protected void executeInternal(JobExecutionContext jobExecutionContext)
throws JobExecutionException {
someTask.doSomething();
anotherTask.doSomethingElse();
}
2. MethodInvokingJobDetailFactoryBean:
You can place following codes instead of above to create JobDetail as shown below:
<bean id="sampleQuartzJobBean"
class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBea
n">
</bean>
Trigger
1. Simple Trigger: It allows to set start time, end time and repeat interval to run the job.
Example:
<bean id="simpleTriggerBean"
class="org.springframework.scheduling.quartz.SimpleTriggerBean">
</bean>
2. Cron Trigger: It allows UNIX cron expression to specify the dates and times to run job.
<bean id="sampleQuartzJobCronTrigger"
class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="sampleQuartzJobBean" />
<property name="cronExpression" value="0/15 * * * * ?" />
</bean>
Corn Expression:
Cron-Expressions are used to configure instances of CronTrigger. Cron-Expressions are strings
that are actually made up of seven sub-expressions, that describe individual details of the
schedule. These sub-expression are separated with white-space, and represent:
1. Seconds
2. Minutes
3. Hours
4. Day-of-Month
5. Month
6. Day-of-Week
7. Year (optional field)
CronTrigger Example 1 - an expression to create a trigger that simply fires every 5 minutes
“0 0/5 * * * ?”
“10 0/5 * * * ?”
CronTrigger Example 3 - an expression to create a trigger that fires at 10:30, 11:30, 12:30, and
13:30, on every Wednesday and Friday.
“0 30 10-13 ? * WED,FRI”
CronTrigger Example 4 - an expression to create a trigger that fires every half hour between the
hours of 8 am and 10 am on the 5th and 20th of every month. Note that the trigger will NOT fire
at 10:00 am, just at 8:00, 8:30, 9:00 and 9:30
SchedulerFactoryBean integrates both Job Detail and Trigger both together as shown below:
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="jobDetails">
<list>
</list>
</property>
</bean>
JDK Timer
1. Create a Dynamic Web Project named JDKTimerDemo and add following Jar files both
in java build path and deployment assembly.
2. Create a package named scheduling.tasks and create a class named SomeTask.java inside
it and put following codes inside it:
package scheduling.tasks;
import org.springframework.scheduling.annotation.Scheduled;
public SomeTask()
{
System.out.println("Executed...");
}
//@Scheduled(cron = "* * 1 * * ?")
@Scheduled(fixedRate=6000)
public void doSomething() {
}
3. web.xml should contain following codes:
<?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"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID"
version="2.5">
<display-name>JDKTimerDemo</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<display-name>Sample Scheduling Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
4. appliCationContext.xml should contain following codes:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<task:annotation-driven />
</beans>
5. Create a jsp file named index.jsp inside WebContent folder and put following codes
inside it:
<html>
<body>
<h2>Hello Friends!</h2>
<h3>Check out the console for sysouts from the scheduled
jobs.</h3>
</body>
</html>
Note: Run above application Right Click Project-> Run As-> Run on Server-.
Select Tomcat. doSomething( ) method is executed every 5 second.
Conclusion:
It can be seen that for an application requiring basic scheduling support, it is wise to
choose JDK Timer, however for applications that requires more sophisticated scheduling
support; it is preferable to choose Quartz.
Reference:
1. http://javabeat.net/spring-job-scheduling-support-with-jdk-timer-and-quartz/
2. http://www.boykosissues.com/2012/09/simple-spring-batch-web-app-with-
maven.html
3. http://www.concretepage.com/spring-4/spring-4-quartz-2-scheduler-integration-
annotation-example-using-javaconfig
4. http://www.quartz-scheduler.org/documentation/quartz-2.x/tutorials/tutorial-lesson-
06.html
Spring Batch
Spring Batch provides many reusable functions adopted from the Spring framework and
customized for batch applications to perform common batch activity( such as split processing of
huge volumes of data, logging, transaction management etc). Spring Batch can work in
conjunction with a Scheduler such as Quartz, however cannot replace a scheduler. Spring Batch
is not a scheduling framework.
1. Create a Dynamic Web Project named BatchScheduleDemo and include following jars
both in Java Build Path and Deployment Assembly
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
@Override
public RepeatStatus execute(StepContribution paramStepContribution,
ChunkContext paramChunkContext) throws Exception {
System.out.println("Batch Task Executed...");
return RepeatStatus.FINISHED;
}
}
5. Keep following codes inside MyLauncher.java
package ch8.batch.demo;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.JobParametersBuilder;
import org.springframework.batch.core.launch.JobLauncher;
class MyLauncher {
private JobLauncher jobLauncher;
@Override
public String toString() {
return "Report [id=" + id + ", Impressions=" + Impressions + "]";
}
}
7. Create a java file named CustomWriter.java inside ch8.batch.writer package and keep
following codes inside it:
package ch8.batch.writer;
import java.util.List;
import ch8.batch.model.Report;
import org.springframework.batch.item.ItemWriter;
@Override
public void write(List<? extends Report> items) throws Exception {
System.out.println("writer..." + items.size());
for(Report item : items){
System.out.println(item);
}
8. Inside WEB-INF folder create a folder named resources and inside resources folder
create an Excel file named report.csv with following information.
9. Inside WEB-INF folder create two xml files namely applicationContext.xml and
jobConfig.xml
10. applicationContext.xml should conisists of following codes:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="transactionManager"
class="org.springframework.batch.support.transaction.ResourcelessTransactionMa
nager" />
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.MapJobRepositoryFacto
ryBean">
<property name="transactionManager" ref="transactionManager" />
</bean>
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
</beans>
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names"
value="id,impressions" />
</bean>
</property>
<property name="fieldSetMapper">
<bean
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName"
value="report" />
</bean>
</property>
</bean>
</property>
</bean>
<batch:job id="reportJob">
<batch:step id="step1">
<batch:tasklet>
<batch:chunk reader="cvsFileItemReader" writer="customWriter"
commit-interval="10">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
<batch:job id="simpleJob">
<batch:step id="simpleTaskletStep">
<batch:tasklet ref="mytasklet" />
</batch:step>
</batch:job>
<task:scheduled-tasks>
<task:scheduled ref="myLauncher1" method="launch" fixed-delay="6000" />
<task:scheduled ref="myLauncher" method="launch" cron="*/5 * * * * *" />
</task:scheduled-tasks>
</beans>
12. Your web.xml should consists of following codes:
<?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"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID"
version="2.5">
<display-name>BatchScheduleDemo</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/jobConfig.xml</param-value>
</context-param>
<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-
class>
</listener>
</web-app>
13. Inside, WebContent folder create a jsp file named index.jsp and keep following codes
inside it:
<%@ 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>Insert title here</title>
</head>
<body>
<h1>Batch Demo Program</h1>
</body>
</html>
In above program, we have created two batch jobs namely simpleJob and reportJob
Simple Job we have implemented inside SimpleTasklet class execute method, which
simply prints "Batch Task Executed" message. On the other hand, "reportJob" reads
items from report.csv file and prints it on the console. Printing items on the console is
done by CustomWriter class and excel field name is mapped to java POJO by Report
class. These two classes are for reportJob whereas SimpleTasklet is for simpleJob.
MyLauncher is required by both jobs namely "reportJob" and "simpleJob".
For each job, we should have separate launcher as shown below:
Note:
TaskScheduler
TaskScheduler helps to schedule a job. Below we are going to develop a sample application that
uses TaskScheduler.
1. Create a Dynamic Web Project named TaskSchedulingDemo and include following jar
files in both Java Build Path Delployment Assembly
2. Crate a package named ch8.demo.scheduling inside src folder and create a class called
TestBean and keep following codes inside it: testMethod ( ) contains code which is going
to get invoked periodically by the scheduler.
package ch8.demo.scheduling;
import org.springframework.stereotype.Component;
@Component("myBean")
public class TestBean {
3. Create an XML file named appConfig.xml inside WEB-INF folder and keep following
codes inside 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:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-4.0.xsd">
<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="myBean" method="testMethod" fixed-
delay="5000" />
</task:scheduled-tasks>
</beans>
4. Create following codes inside web.xml file:
<?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"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID"
version="2.5">
<display-name>TaskSchedulingDemo</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/appConfig.xml</param-value>
</context-param>
<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
5. Create a file named index.jsp inside WebContent folder and keep following codes inside
it:
<%@ 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>Insert title here</title>
</head>
<body>
<h1> Task Scheduling Demo</h1>
</body>
</html>
Following codes configure a scheduler which schedules some tasks to run at some point in the
future.
<task:scheduled-tasks scheduler="myScheduler">
<task:scheduled ref="myBean" method="testMethod" fixed-delay="5000" />
</task:scheduled-tasks>
task:scheduled defines a new task. Attribute ‘ref’ refers to the bean whose method (referred by
attribute ‘method’) will be called on specific time condition. fixed-delay specifies the time in
milliseconds between the completion of first task and start of next task.
Here we are saying that myBean.printMessage() will be by scheduler periodically, with 5
seconds delay between completion of first task and start of next task.
initial-delay parameter specifies the number of milliseconds to wait before the first execution of
the method.
fixed-rate specifies the number of milliseconds between each method start , regardless of how
long method takes to complete.
cron provides more fine-grained control on task execution. Here we have configured task3 to run
every 5 seconds but only on weekdays.
Reference:
1. http://websystique.com/spring/spring-job-scheduling-using-xml-configuration/