This post use the same Counter project of the previous post of Singleton Session Bean Basics with Example - Counter.
@Singleton
public class SingletonBean
{
public void setState()
{
//CODE
}
}
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.
@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
}
}
LockBean.java
Template.xhtml
LockTest.xhtml
LockTestBean.java
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.
And this all about this post.
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 { ... }
public class SingletonBean { ... }
The following is container-managed concurrency example with the presence of @ConcurrencyManagement annotation.
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER)
@Singleton
public class SingletonBean { ... }
@Singleton
public class SingletonBean { ... }
The following is bean-managed concurrency example with the presence of @ConcurrencyManagement annotation.
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
@Singleton
public class SingletonBean { ... }
@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;
}
}
@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;
}
}
<?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>
<!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>
<?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>
<!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;
}
}
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;
}
}
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