Monday 30 December 2013

Spring Security: Custom UserDetails Service and Login form

To get familiar with key concepts of Spring Security, refer to my previous post. In this post, we will be writing a custom UserDetails service which will talk to the database [hibernate + MySQL] and fetch the user authentication information.

Setup

This is how my project structure looks like:

Dependencies

If you are using maven, following is the pom.xml file containing the list of all the dependencies:

 4.0.0
 SpringSecurity
 SpringSecurity1
 war
 0.0.1-SNAPSHOT
 SpringSecurity1 Maven Webapp
 http://maven.apache.org
 
  
   junit
   junit
   3.8.1
   test
  
  
   org.springframework
   spring-orm
   3.2.0.RELEASE
  
  
   org.springframework
   spring-webmvc
   3.2.0.RELEASE
  
  
   org.springframework.security
   spring-security-web
   3.2.0.RELEASE
  
  
   org.springframework.security
   spring-security-config
   3.2.0.RELEASE
  
  
   org.springframework.security
   spring-security-taglibs
   3.2.0.RELEASE
  
  
   jstl
   jstl
   1.2
   compile
  
  
   taglibs
   standard
   1.1.2
   compile
  
  
   javax
   javaee-api
   7.0
  
  
   org.hibernate
   hibernate-core
   3.6.10.Final
  
  
   mysql
   mysql-connector-java
   5.1.26
  
  
   commons-dbcp
   commons-dbcp
   20030825.184428
  
  
   commons-pool
   commons-pool
   20030825.183949
  
  
   commons-collections
   commons-collections
   3.2.1
  
  
   javassist
   javassist
   3.12.1.GA
  
  
   org.codehaus.jackson
   jackson-mapper-asl
   1.9.12
  
 
 
  SpringSecurity1
 



Configuring web.xml


  SpringSecurity1
  
    /WEB-INF/jsp/index.jsp
  
  
    spring
    
            org.springframework.web.servlet.DispatcherServlet
        
    1
  
  
    spring
    /
  
  
    
                  org.springframework.web.context.ContextLoaderListener
                
  
  
    contextConfigLocation
    
   /WEB-INF/spring-servlet.xml,
   /WEB-INF/spring-security.xml
  
  
  
    springSecurityFilterChain
    
                  org.springframework.web.filter.DelegatingFilterProxy
                
  
  
    springSecurityFilterChain
    /*
  

This provides a hook into the Spring Security web infrastructure. DelegatingFilterProxy is a Spring Framework class which delegates to a filter implementation which is defined as a Spring bean in your application context. In this case, the bean is named "springSecurityFilterChain", which is an internal infrastructure bean created by the namespace to handle web security. Note that you should not use this bean name yourself. Once you’ve added this to your web.xml, you’re ready to start editing your application context file. Web security services are configured using the <http> element.

Configuring spring-security.xml



 
  
  
   
  
 

 
  
  
 


The <intercept-url> element defines a pattern which is matched against the URLs of incoming requests using an ant path style syntax. In general the xml states that we want to log in to the application using a form with username and password, and that we want a logout URL registered which will allow us to log out of the application.The access attribute defines the access requirements for requests matching the given pattern. With the default configuration, this is typically a comma-separated list of roles, one of which a user must have to be allowed to make the request.Here, it states that we want all URLs matching the pattern '/user/**' to be secured, requiring the role ROLE_USER to access them.Similarly, we want all URls matching the pattern '/admin/**' require the role ROLE_ADMIN to access them.

If a form login isn't prompted by an attempt to access a protected resource, the default-target-url option comes into play. This is the URL the user will be taken to after successfully logging in, and defaults to "/". One can also configure things so that the user always ends up at this page (regardless of whether the login was "on-demand" or they explicitly chose to log in) by setting the always-use-default-target attribute to "true". The authentication-failure-attribute defines the URL that  shows the custom access denied page if the user fails to authenticate himself.

The logout-success-url specifies the URL which will be presented to the user once the user has logged out.

Authentication manager will handle all the authentication requests. All authentication-provider elements must be children of the <authentication-manager> element, which creates a ProviderManager and registers the authentication providers with it. The user-service-ref attribute for the authentication provider says that we have a custom implementation of Spring Security’s UserDetailsService, called "authService" defined in the application context file. UserDetailsService is a special interface which has the only method which accepts a String-based username argument and returns a UserDetails.

UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;


UserDetails is a core interface in Spring Security which represents a principal, but in an extensible and application-specific way. Think of UserDetails as the adapter between your own user database and what Spring Security needs inside the SecurityContextHolder. The returned UserDetails is an interface that provides getters that guarantee non-null provision of authentication information such as the username, password, granted authorities and whether the user account is enabled or disabled.

Note that UserDetailsService is purely a DAO for user data and performs no other function other than to supply that data to other components within the framework. In particular, it does not authenticate the user, which is done by the AuthenticationManager.

Configuring spring-servlet.xml




 

 

 
  
   
    
   
  
 

 
  
  
 

 
  
  
  
  
 

 
  
  
   classpath:hibernate.cfg.xml
  
  
   org.hibernate.cfg.AnnotationConfiguration
   
  
 

 

 

 
  
 

Here, we have defined a bean authService which implements the interface UserDetailsService. It also has hibernate configurations as we will be storing the user authentication information in my database [MySQL]. 

hibernate.cfg.xml contents


 
  
  
  1

  
  org.hibernate.dialect.MySQLDialect

  
  org.hibernate.cache.NoCacheProvider

  
  true

  create
  
  
  
 

MyUser model object

package com.spring.security.domain;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "userdetails")
public class MyUser {

 @Id
 @GeneratedValue
 private int userId;
 private String username;
 private String password;
 private String role;

 public String getUsername() {
  return username;
 }

 public void setUsername(String username) {
  this.username = username;
 }

 public String getPassword() {
  return password;
 }

 public void setPassword(String password) {
  this.password = password;
 }

 public int getUserId() {
  return userId;
 }

 public void setUserId(int userId) {
  this.userId = userId;
 }

 public String getRole() {
  return role;
 }

 public void setRole(String role) {
  this.role = role;
 }

}
It has username, password and roles attributes which will be stored in database.

UserDetailsService Implementation

package com.spring.security.service.impl;

import java.util.ArrayList;
import java.util.Collection;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.spring.security.dao.IUserDao;
import com.spring.security.domain.MyUser;
import com.spring.security.service.IAuthService;

@Service
public class AuthServiceImpl implements IAuthService, UserDetailsService {

 @Autowired
 IUserDao userDao;

 @Transactional
 @Override
 public UserDetails loadUserByUsername(String username)
   throws UsernameNotFoundException {

  MyUser details = userDao.getUser(username);
  Collection authorities = new ArrayList();
  SimpleGrantedAuthority userAuthority = new SimpleGrantedAuthority(
    "ROLE_USER");
  SimpleGrantedAuthority adminAuthority = new SimpleGrantedAuthority(
    "ROLE_ADMIN");
  if (details.getRole().equals("user"))
   authorities.add(userAuthority);
  else if (details.getRole().equals("admin")) {
   authorities.add(userAuthority);
   authorities.add(adminAuthority);
  }
  UserDetails user = new User(details.getUsername(),
    details.getPassword(), true, true, true, true, authorities);
  return user;
 }
}

As u can see, AuthServiceImpl implements UserDetailsService interface. This needs to override a loadUserByUsername() method that takes username as an argument and returns a UserDetails object. We fetch a MyUser object from DB that stores the username,password and role for a user. It checks if role present is 'user' it adds a ROLE_USER authority to the authorities list. If it is 'admin' it adds ROLE_ADMIN as well as ROLE_USER authorities. It then creates a UserDetails object using a constructor passing the appropriate arguments.

Controller

package com.spring.security.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.spring.security.domain.MyUser;
import com.spring.security.service.IUserService;

@Controller
public class HelloController {

 @Autowired
 IUserService userService;

 @RequestMapping(value = "/user/welcome", method = RequestMethod.GET)
 public String printWelcomeUser() {
  return "hello";
 }

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

 @RequestMapping(value = "/login", method = RequestMethod.GET)
 public String getLoginPage(Model model) {
  return "login";
 }

 @RequestMapping(value = "/home", method = RequestMethod.GET)
 public String getHomePage(Model model) {
  return "hello";
 }

 @RequestMapping(value = "/accessdenied", method = RequestMethod.GET)
 public String getFailurePage(Model model) {
  return "failure";
 }

 @RequestMapping(value = "/logout", method = RequestMethod.GET)
 public String getLogoutPage(Model model, HttpServletRequest req) {
  req.getSession().invalidate();
  return "logout";
 }

 @RequestMapping(value = "/user", method = RequestMethod.POST)
 @ResponseBody
 String saveUser(@RequestBody MyUser user, HttpServletResponse response) {
  System.out.println("User:" + user.getUsername());
  userService.saveUser(user);
  response.setStatus(201);
  return "success";
 }
}

UserDao

package com.spring.security.dao.impl;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.spring.security.dao.IUserDao;
import com.spring.security.domain.MyUser;

@Repository
public class UserDaoImpl implements IUserDao {

 @Autowired
 private SessionFactory sessionFactory;

 @Override
 public MyUser getUser(String username) {
  Session session = sessionFactory.getCurrentSession();
  Criteria criteria = session.createCriteria(MyUser.class);
  criteria.add(Restrictions.eq("username", username));
  MyUser user = (MyUser) criteria.uniqueResult();
  return user;
 }

 @Override
 public void saveUser(MyUser user) {
  sessionFactory.getCurrentSession().save(user);
 }
}


Views

admin.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>




Hello admin


Welcome admin !

Logout
Simple welcome page that displays Welcome Admin !The default logout URL is /j_spring_security_logout, but you can set it to something else using the logout-url attribute in spring-security.xml.

hello.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
 pageEncoding="ISO-8859-1"%>




Hello user



 

Welcome User !

Logout
Simple welcome page that displays Welcome User !

index.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
 pageEncoding="ISO-8859-1"%>





Insert title here



 

Hello All

Home page

login.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>



 

Login to Spring Security App

Username:
Password:
 
 
Post request to  /j_spring_security_check authenticates the user. The username parameter is set to j_username and password to j_password. 

logout.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
 pageEncoding="ISO-8859-1"%>




Logout


 

You have been successfully logged out !

Go back to login page
Logout page to which a user is directed after successful log out.

failure.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
 pageEncoding="ISO-8859-1"%>




 

Unauthenticated user !!

Access denied! Go back to login page
Access denied page which is shown to the user if he fails to authenticate.


Demo

This is my database state.



When we try to access /user/welcome resource, we will be prompted with our custom login page.




When we log in as user1, we will be granted ROLE_USER authority and hence we will not be able to access /admin/welcome which requires ROLE_ADMIN authority.



If we enter incorrect credentials, we will be presented with our custom access denied web page. 



You can view/download the complete source code from here.

Thanks !!

Friday 27 December 2013

Spring Security : User Details in properties file

In my previous post, a simple example demonstrating Spring Security was presented wherein the user details were provided int the spring-security.xml file. 

In this example, the user details will be stored in an external properties file. Rest of the configuration remains the same. The properties file should look like this -

username=password,grantedAuthority[,grantedAuthority][,enabled|disabled]

Sample properties file
user1=user1password,ROLE_USER,ROLE_ADMIN,enabled
user2=user2password,ROLE_USER,enabled

Configuring spring-servlet.xml



 
  
  
  
  
 

 
  
   
  
 

Notice that in this case we provide users.properties as a value for the attribute 'properties' for the user-service.

You can download the complete source code for this example from here.

Spring Security Introduction : Basic Authentication Example

Introduction

Spring Security is a major module in Spring Distribution. It is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements.

Before going forward lets have an overview of some of the key concepts:

  • Web/HTTP Security - It is the most complex part.It sets up the filters and related service beans used to apply the framework authentication mechanisms, to secure URLs, to render login and error pages and much more.
  • Business Object (Method) Security - It provides options for securing the service layer.
  • AuthenticationManager - It handles authentication requests from other parts of the framework.
  • AccessDecisionManager - It provides access decisions for web and method security. A default one will be registered, but you can also choose to use a custom one, declared using normal Spring bean syntax.
  • AuthenticationProviders - They provide mechanisms against which the authentication manager authenticates users. The namespace provides support for several standard options and also a means of adding custom beans declared using a traditional syntax.
  • UserDetailsService - It is closely related to authentication providers, but often also required by other beans.It is used to provide authentication information.
In this example, the user authentication information is maintained in xml configuration file for spring security.


Setup

This is how the final project structure looks like -



Dependencies

If you are using maven, following are the dependencies which will be required -

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>3.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>3.2.0.RELEASE</version>
</dependency>

These are the minimal set of dependencies for Spring Security. Using additional features will require addition of appropriate project modules.

Configuring web.xml



    SpringSecurity1
    
        /WEB-INF/jsp/index.jsp
    
 
    
        spring
        
            org.springframework.web.servlet.DispatcherServlet
        
        1
    
    
        spring
        /
     
    
    
  
                  org.springframework.web.context.ContextLoaderListener
                
 
 
 
  contextConfigLocation
  
   /WEB-INF/spring-servlet.xml,
   /WEB-INF/spring-security.xml
  
 
 
 
 
  springSecurityFilterChain
  
                  org.springframework.web.filter.DelegatingFilterProxy
                
 
 
 
  springSecurityFilterChain
  /*
 
This provides a hook into the Spring Security web infrastructure. DelegatingFilterProxy is a Spring Framework class which delegates to a filter implementation which is defined as a Spring bean in your application context. In this case, the bean is named "springSecurityFilterChain", which is an internal infrastructure bean created by the namespace to handle web security. Note that you should not use this bean name yourself. Once you’ve added this to your web.xml, you’re ready to start editing your application context file. Web security services are configured using the <http> element.

Don't forget to include spring-security.xml file for the Context Configuration location.

Configuration : spring-security.xml




 
  
  
  
  
 

 
  
   
    
   
  
 



This is the configuration file where we provide all the spring security configurations. <http> element is the parent for all web-related namespace functionality. The <intercept-url> element defines a patternwhich is matched against the URLs of incoming requests using an ant path style syntax. All the URLs beginning with welcome will be intercepted in this case. It says that we want all URLs matching the pattern '/welcome**' to be secured, requiring the role ROLE_USER to access them, we want to log in to the application using a form with username and password, and that we want a logout URL registered which will allow us to log out of the application.
The access attribute defines the access requirements for requests matching the given pattern. With the default configuration, this is typically a comma-separated list of roles, one of which a user must have to be allowed to make the request.The logout-success-url specifies the URL which will be presented to the user once the user has logged out.

If a form login isn’t prompted by an attempt to access a protected resource, the default-target-url option comes into play. This is the URL the user will be taken to after successfully logging in, and defaults to "/". One can also configure things so that the user always ends up at this page (regardless of whether the login was "on-demand" or they explicitly chose to log in) by setting the always-use-default-targetattribute to "true".


The <authentication-provider> element creates a DaoAuthenticationProvider bean and the <user-service> element creates an InMemoryDaoImpl. All authentication-provider elements must be children of the <authentication-manager> element, which creates a ProviderManager and registers the authentication providers with it.

The configuration above defines two users, their passwords and their roles within the application (which will be used for access control). When a user enters login information, his username and password are matched with those specified in user-service. If successfully authenticated, the mentioned attributes are linked to the user. These attributes decide whether a user is authorized to make certain requests or not.

Setting up the Controller and spring-servlet.xml 

This is my controller.
package com.spring.security;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class HelloController {

 @RequestMapping(value = "/welcome", method = RequestMethod.GET)
 public String printWelcome(Model model) {
  model.addAttribute("message", "Spring Security says Hello !");
  return "hello";
 }

 @RequestMapping(value = "/login", method = RequestMethod.GET)
 public String getLoginPage(Model model) {
  return "login";
 }

 @RequestMapping(value = "/home", method = RequestMethod.GET)
 public String getHomePage(Model model) {
  return "index";
 }

 @RequestMapping(value = "/logout", method = RequestMethod.GET)
 public String getLogoutPage(Model model, HttpServletRequest req) {
  req.getSession().invalidate();
  return "logout";
 }
}

spring-servlet.xml



 

 

 
  
  
 


Views

hello.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
 pageEncoding="ISO-8859-1"%>




Insert title here



 

Message : ${message}

Logout
The default logout URL is /j_spring_security_logout, but you can set it to something else using the logout-url attribute.

logout.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
 pageEncoding="ISO-8859-1"%>




Logout


 

You have been successfully logged out !

Go back to login page
The URL where you will find the security login page is '/spring_security_login'.

index.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
 pageEncoding="ISO-8859-1"%>





Home



 

Home Page

This is a simple home page


Demo

When you try to access '/SpringSecurity1/welcome' you will be prompted with a Spring Security login page. You might be wondering where the login form came from when you were prompted to log in, since we made no mention of any HTML files or JSPs. In fact, since we didn’t explicitly set a URL for the login page, Spring Security generates one automatically, based on the features that are enabled and using standard values for the URL which processes the submitted login, the default target URL the user will be sent to after logging in and so on.






With correct credentials you will be forwarded to the welcome page



An unsuccessful login will result in a Bad Credentials page.

This was a simple example to give you an overview on Spring Security. In my upcoming posts we will be looking into details many other features provided by Spring Security. You can download the source code of this example from here. 

Thanks and Happy coding !


Monday 16 December 2013

Fork / Join Framework : RecursiveTask Example

In my previous post, I had demonstrated the use of RecursiveAction class for the Fork / Join framework. Continuing that here in this post, an example for the RecursiveTask class has been explained. 

Two methods inherited from ForkJoinTask have been used:

1. fork() - It allows a ForkJoinTask to be planned for asynchronous execution. This allows a new ForkJoinTask to be launched from an existing one.

2. join() - It returns the result of the computation when it is done

The task here is to find the sum of all the elements in an array. Let's have a look at this: 


package com.fork.join.task;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class ForkJoinSumTask {

 Random random = new Random();

 public void fillArray(int[] array) {
  for (int i = 0; i < array.length; i++) {
   array[i] = array[i] = random.nextInt(10000);
  }
 }

 public static void main(String[] args) {
  ForkJoinSumTask sum = new ForkJoinSumTask();
  int[] array = new int[20_00_00_000];
  sum.fillArray(array);

  long count;
  long start1;

  // Sequential process to get the sum of the elements in array
  for (int j = 0; j < 20; j++) {
   count = 0;
   start1 = System.currentTimeMillis();
   for (long i = 0; i < (long) array.length; i++) {
    count = (count + array[(int) i]);
   }

   System.out.println("Addition Result: " + count);
   System.out.println("Sequential processing time: "
     + (System.currentTimeMillis() - start1) + " ms");

  }
  System.out.println("Parallel processing time");
  System.out.println("Number of processors available: "
    + Runtime.getRuntime().availableProcessors());

  ForkJoinPool fjpool = new ForkJoinPool(Runtime.getRuntime().availableProcessors());
  // Default parallelism level
  // =
  // Runtime.getRuntime().availableProcessors()
  long start2;

  for (int i = 0; i < 20; i++) {
   RecursiveSumTask task = new RecursiveSumTask(array, 0, array.length);
   start2 = System.currentTimeMillis();
   System.out.println("Addition Result: " + fjpool.invoke(task));
   System.out.println("Parallel processing time: "
     + (System.currentTimeMillis() - start2) + " ms");
  }

  System.out
    .println("Number of steals: " + fjpool.getStealCount() + "\n");
 }
}

class RecursiveSumTask extends RecursiveTask {
 private static final long serialVersionUID = 1L;
 final int low;
 final int high;
 private int[] array;
 final int splitSize = 1000_00_000; // Some threshold size to spit the task

 RecursiveSumTask(int[] array, int from, int to) {
  this.low = from;
  this.high = to;
  this.array = array;
 }

 @Override
 protected Long compute() {
  long count = 0L;
  List> forks = new ArrayList<>();

  if (high - low > splitSize) {
   // task is huge so divide in half
   int mid = (low + high) / 2;

   // Divided the given task into task1 and task2
   RecursiveSumTask task1 = new RecursiveSumTask(array, low, mid);
   forks.add(task1);
   task1.fork();

   RecursiveSumTask task2 = new RecursiveSumTask(array, mid, high);
   forks.add(task2);
   task2.fork();

  } else {
   // Calculating sum of the given array range
   for (int i = (int) low; i < high; i++) {
    count = count + array[i];
   }
  }

  // Waiting for the result
  for (RecursiveTask task : forks) {
   count = count + task.join();
  }

  return count;
 }
}

Note that RecursiveSumTask extends ForkJoinTask in this case. The compute() has a return type unlike ForkJoinAction whose compute() did not have a return type.

During sequential processing, the whole array is scanned sequentially to perform addition of the elements present in it. 

For parallel execution, again a ForkJoinPool is created. Runtime.getRuntime().availableProcessors() will return the number of available processors available with the system. fork() is performed on each recursive task and result of each such task is added using the join() function join() returns the computation result for the task. 

Some threshold value (array size in this case) is used to decide whether the computation is to be performed directly or is to be divided into sub tasks (ForkJoinTasks).


Observations: 


Addition Result: 999844939360

Sequential processing time: 122 ms
Addition Result: 999844939360
Sequential processing time: 126 ms
Addition Result: 999844939360
Sequential processing time: 120 ms
Addition Result: 999844939360
Sequential processing time: 120 ms
Addition Result: 999844939360
Sequential processing time: 120 ms
Addition Result: 999844939360
Sequential processing time: 121 ms
Addition Result: 999844939360
Sequential processing time: 120 ms
Addition Result: 999844939360
Sequential processing time: 119 ms
Addition Result: 999844939360
Sequential processing time: 120 ms
Addition Result: 999844939360
Sequential processing time: 121 ms

Parallel processing 
Number of processors available: 4

Addition Result: 999844939360
Parallel processing time: 42 ms
Addition Result: 999844939360
Parallel processing time: 49 ms
Addition Result: 999844939360
Parallel processing time: 40 ms
Addition Result: 999844939360
Parallel processing time: 38 ms
Addition Result: 999844939360
Parallel processing time: 38 ms
Addition Result: 999844939360
Parallel processing time: 38 ms
Addition Result: 999844939360
Parallel processing time: 39 ms
Addition Result: 999844939360
Parallel processing time: 38 ms
Addition Result: 999844939360
Parallel processing time: 38 ms
Addition Result: 999844939360
Parallel processing time: 38 ms
Number of steals: 30


For parallel processing, the processing time is nearly 1/3 rd of the time required for sequential processing. 

CPU Utilization: 

CPU usage was found to be around 25 % during sequential execution. 

















For parallel processing , CPU usage was on an average around 58 %
















There is a clear increase in the CPU usage for parallel processing. The computation involved in this case was pretty simple (sum of the array elements). Better results can be experienced if the tasks involved are more complex.

The Fork / Join framework is more useful in cases where sequential operations are complex and time consuming. An appropriate threshold in such situations can result in much better CPU utilization. For short tasks, Fork/Join framework is not recommended.

You can also refer my previous blog on Fork/Join Framework for more information

Thanks and Happy Coding !

Java 7 - Fork / Join Framework example

Fork / Join as the name suggests is designed for work that can be broken into smaller pieces recursively. It is a new addition to the JDK 1.7 to support parallelism. It is an implementation of the ExecutorService interface that helps you take advantage of multiple processors.The goal is to use all the available processing power to enhance the performance of your application.

The Fork/Join framework is designed to make divide-and-conquer algorithms easy to parallelize. That type of algorithms is perfect for problems that can be divided into two or more sub-problems of the same type. They use recursion to break down the problem to simple tasks until these become simple enough to be solved directly. The solutions to the sub-problems are then combined to give a solution to the original problem.


The center of the fork/join framework is the ForkJoinPool class, an extension of the AbstractExecutorService class. ForkJoinPool implements the core work-stealing algorithm and can execute ForkJoinTask processes.It is similar to the MapReduce approach used to paralyze tasks. Difference is that Fork/Join tasks will subdivide themselves into smaller tasks only if necessary (if too large), whereas MapReduce algorithms divide up all the work into portions as the first step of their execution.


Basic Algorithm:


if(the job is small enough)
{
   compute directly
}
else
{
   split the work in two pieces (fork)
   invoke the pieces and join the results (join)
}


A ForkJoinTask is an abstract base class for tasks that run within a ForkJoinPool. A ForkJoinTask is a thread-like entity that is much lighter weight than a normal thread. Huge numbers of tasks and subtasks may be hosted by a small number of actual threads in a ForkJoinPool, at the price of some usage limitations.

There are two specialized subclasses of the ForkJoinTask :

1. RecursiveAction : It is to be used when you don’t need the task to return a result, for example, when the task works on positions of an array, it doesn’t return anything because it worked on the array. The method you should implement in order to do the job is compute():void, notice the void return.

2. RecursiveTask : It is to be used when your tasks return a result. For example, when computing addition of elements in an array, each task must return the number it computed in order to join them and obtain the general solution. The method you should implement in order to do the job is compute():V, where V is the type of return; for example in calculating the sum of integer elements in an array, V may be java.lang.Integer.

In this post, I ll be demonstrating you an example for the RecursiveAction. The task here is to fill the array elements with a random value. Here's the code :

package com.fork.join.action;

import static java.util.Arrays.asList;

import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

public class ForkJoinRandomFillAction {
 Random random = new Random();

 public void loadArray(int[] array) {
  for (int i = 0; i < array.length; i++) {
   array[i] = random.nextInt(10000); // Generates numbers from 0 to
            // 10000
  }
 }

 public static void main(String[] args) {

  ForkJoinRandomFillAction sort = new ForkJoinRandomFillAction();

  int arrayLength = 2_00_00_0000;
  int array[] = new int[arrayLength];

  // No. of times sequential & Parallel operation should be performed
  final int iterations = 10;

  for (int i = 0; i < iterations; i++) {
   long start = System.currentTimeMillis();
   sort.loadArray(array);

   System.out.println("Sequential processing time: "
     + (System.currentTimeMillis() - start) + " ms");

  }

  System.out.println("Number of processor available: "
    + Runtime.getRuntime().availableProcessors());

  ForkJoinPool fjpool = new ForkJoinPool();
  // Default parallelism level
  // Runtime.getRuntime().availableProcessors()

  for (int i = 0; i < iterations; i++) {
   // Create a task with the complete array
   RecursiveAction task = new RandomFillAction(array, 0, array.length);
   long start = System.currentTimeMillis();
   fjpool.invoke(task);

   System.out.println("Parallel processing time: "
     + (System.currentTimeMillis() - start) + " ms");
  }

  System.out
    .println("Number of steals: " + fjpool.getStealCount() + "\n");
 }
}

class RandomFillAction extends RecursiveAction {
 private static final long serialVersionUID = 1L;
 final int low;
 final int high;
 private int[] array;
 final int splitSize = 2000000; // Some threshold size to spit the task

 public RandomFillAction(int[] array, int low, int high) {
  this.low = low;
  this.high = high;
  this.array = array;
 }

 @Override
 protected void compute() {
  if (high - low > splitSize) {
   // task is huge so divide in half
   int mid = (low + high) / 2;
   invokeAll(asList(new RandomFillAction(array, low, mid),
     new RandomFillAction(array, mid, high)));
  } else {
   // Some calculation logic
   Random random = new Random();
   for (int i = low; i < high; i++) {
    array[i] = random.nextInt(10000);
   }
  }
 }
}

For sequential processing the whole array is filled sequentially with random values. During parallel processing,
we first create a ForkJoinPool which will execute the ForkJoinTask. The default parallelism level for the ForkJoinPool is set to the no. of processors available with the system.Runtime.getRuntime().availableProcessors() will get this value.

Note that the RandomFillAction extends the RecursiveAction class and hence it needs to overwrite the default compute function which has the logic to either split the task or to perform the computation.

Some threshold value (array size in this case) is used to decide whether the computation is to be performed directly or is to be divided into sub tasks (ForkJoinTasks).


Observations:


Sequential processing time: 2316 ms
Sequential processing time: 2287 ms
Sequential processing time: 2287 ms
Sequential processing time: 2293 ms
Sequential processing time: 2287 ms
Sequential processing time: 2295 ms
Sequential processing time: 2292 ms
Sequential processing time: 2291 ms
Sequential processing time: 2291 ms
Sequential processing time: 2292 ms
Number of processor available: 4
Parallel processing time: 705 ms
Parallel processing time: 650 ms
Parallel processing time: 684 ms
Parallel processing time: 602 ms
Parallel processing time: 659 ms
Parallel processing time: 737 ms
Parallel processing time: 605 ms
Parallel processing time: 604 ms
Parallel processing time: 602 ms
Parallel processing time: 628 ms

Number of steals: 63

The paralell processing time is only about 30 % of the sequential processing time.


CPU Utilization

Sequential processing - The total CPU usage remained low close to 25 %, never went above 28 %.





















Parallel processing - CPU usage this time around was very attractive, on an average 96 %, hit 100 % at times and never fell below the 90 % mark. 


















This clearly demonstrates the power of Fork / Join framework and its ability to better utilize the CPU processors. 

In my next post, I will be demonstrating an example for the RecursiveTask class.

Thanks and Happy coding !