Thursday, September 8, 2016

Singleton Session Bean Concurrency Control Basics with Example

This post use the same Counter project of the previous post of Singleton Session Bean Basics with Example - Counter.

Here I focus on concurrent access of different methods of a singleton session bean. There are 2 types of concurrency:

1. Container-managed concurrency

2. Bean-managed Concurrency


The javax.ejb.ConcurrencyManagement annotation is used to specify container-managed or bean-managed concurrency for the singleton. 

With @ConcurrencyManagement, a type attribute must be set to either javax.ejb.ConcurrencyManagementType.CONTAINER or javax.ejb.ConcurrencyManagementType.BEAN

If no @ConcurrencyManagement annotation is present on the singleton implementation class, the EJB container default of container-managed concurrency is used.

The following is container-managed concurrency example since no @ConcurrencyManagement annotation is present.

@Singleton
public class SingletonBean { ... }
 

The following is container-managed concurrency example with the presence of  @ConcurrencyManagement annotation.

@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@Singleton
public class
SingletonBean { ... }
 

The following is bean-managed concurrency example with the presence of  @ConcurrencyManagement annotation.


@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
@Singleton
public class
SingletonBean { ... }


1. Container-managed Concurrency

For Container-managed Singleton Session Bean, 2 annotations are used for business methods or @Timeout methods to specify the access level:

1. javax.ejb.Lock  i.e. uses: @Lock

2. javax.ejb.LockType i.e. uses: @Lock(LockType.READ) or @Lock(LockType.WRITE)

@Lock(LockType.READ) - If annotated then the method can be concurrently read or shared. This type needs to explicitly specified in method level or class level.

@Lock(LockType.WRITE) - If annotated then the method is locked to other clients while a client is calling that method. It is default lock type

The following method setState() has WRITE lock.


@Singleton
 public class
SingletonBean  

                  public void setState() 
                  {
                         //CODE
                   }

}


There are another important annotation @AccessTimeout.



@AccessTimeout:


If a method is of locking type WRITE, all client access to this method until the current client finishes its method call or access timeout occurs. This time can be defined by this annotation. When an access timeout occurs, the EJB container (i.e. application server) throws a javax.ejb.ConcurrentAccessTimeoutException.

This annotation is used to specify the time before an exception occurs.  The default time unit is MILLISECONDS. But one can set  unit one of the java.util.concurrent.TimeUnit Contants: NANOSECONDS, MICROSECONDS, MILLISECONDS or SECONDS.

The @AccessTimeout annatation can be applied to both @Lock(LockType.READ) and @Lock(LockType.WRITE) methods. And this annotation can be defined in class level or method level.


In the following example, all methods have access time 120000 milliseconds or 2 minutes except doOperation() method because it has overrides the class level (i.e. default) access timeout value to 1 minute.



@Singleton

@AccessTimeout(value=120000)
 public class
SingletonBean  

                  @Lock(LockType.WRITE)
                  public void setState() 
                  {
                         //CODE
                   }

                 @Lock(LockType.WRITE)
                  public void setName() 
                  {
                         //CODE
                   }

                 @Lock(LockType.WRITE)                  
                 @AccessTimeout(value=60, timeUnit=SECONDS)
                  public void doOperation() 
                  {
                         //CODE
                   }


}



 Example

In the next example, we'll analyze how @Lock(LockType.WRITE) method behaves when multiple client requests come. We'll use Thread.sleep(val1)  inside a @Lock(LockType.WRITE) method which has access time val2 where  
                                                             val2  <  val1

We'll find that a single client call to that method will execute. But  when multiple client call the method, then only the last one will execute and all other call will get exception due to access time is lower than method execution time.

We've the following

-  A Singleton Session bean LockBean.java with 2 methods: getState() and setState(). 

JSF2 Template Template.xhtml

- JSF2 Template client LockTest.xhtml

- Managed Bean LockTestBean.java

LockBean.java

@Singleton
@AccessTimeout(value=2000)
public class LockBean {
  
    private String state = "NO STATE";

    @Lock(LockType.READ)
    public String getState() {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        return state;
    }

    @AccessTimeout(value=3000)
    public void setState(String state) {
        try {
            Thread.sleep(4000);
        } catch (InterruptedException ex) {
          
        }
        this.state = state;
    }
  
  
  
}
 
Template.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html">

    <h:head>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
        <link href="./resources/css/default.css" rel="stylesheet" type="text/css" />
        <link href="./resources/css/cssLayout.css" rel="stylesheet" type="text/css" />
        <title>Counter - A singleton Session Bean Example.</title>
    </h:head>

    <h:body>

        <div id="top" class="top">
            <ui:insert name="top">Top</ui:insert>
        </div>

        <div id="content" class="center_content">
            <ui:insert name="content">Content</ui:insert>
        </div>

    </h:body>

</html>
 
  
LockTest.xhtml

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:h="http://java.sun.com/jsf/html">

    <body>

        <ui:composition template="./Template.xhtml">

            <ui:define name="top">
                Lock Test in Singleton Session Bean
            </ui:define>

            <ui:define name="content">
                <h:form>
                    <h:panelGrid columns="1">
                      
                        <h:panelGroup>
                            <h:outputLabel value="State: " for="state"/>
                            <h:inputText value="#{lockTestBean.status}" id="state"/>
                            <h:commandButton value="Set State" action="#{lockTestBean.setStat()}"/>
                            <h:commandButton value="Get State" action="#{lockTestBean.getStat()}"/>
                        </h:panelGroup>
                        <h:outputText value="#{lockTestBean.message}"/>
                      
                      
                    </h:panelGrid>
                </h:form>
            </ui:define>

        </ui:composition>

    </body>
</html>


 LockTestBean.java

package com.request;

import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
import test.ejb.LockBean;

/**
 *
 * @author Khan.Ashik
 */
@ManagedBean
@RequestScoped
public class LockTestBean {

    @EJB
    private LockBean lBean;
   
    private String message;
   
    private String status;
   
    /**
     * Creates a new instance of LockTestBean
     */
    public LockTestBean() {
    }
   
    public void setStat()
    {
        try
        {
            lBean.setState(status);
            message=lBean.getState();
        }
        catch(Exception e)
        {
            message = e.getMessage();
        }
       
    }
   
    public void getStat()
    {
        try
        {
           
            message=lBean.getState();
        }
        catch(Exception e)
        {
            message = e.getMessage();
        }
       
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }   
   
}

Now deploy and run the Counter application and go to

                    http://localhost:8080/Counter/faces/Locktest.xhtml 

A single client set the state. And it's OK.




 Now 2 clients wants to set the state. 1st one try to 20 and 2nd one is trying to 12. The 1st one will get exception with message Couldn't acquire a lock within 2000 MILLISECONDS and 2nd one get successful.






And this all about this post.

















No comments:

Post a Comment