jee6 学习笔记 6.1 - Singleton EJB

The idea of the test is to print the instance of the singleton ejb for different requests. The singleton ejb is referenced in the request scoped backing bean.

the screen shot of the test page:

jee6 学习笔记 6.1 - Singleton EJB_第1张图片


the page: "/tst/testSingleton.xhtml"
<!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:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:p="http://primefaces.org/ui">
    
<h:head>
    <title>Test EJB3.1 @Singleton</title>
</h:head>

<h:body>
	<h:form>
	    <p:panel header="Test EJB3.1 @Singleton" toggleable="true" style="width:60%">
	    	<h:panelGrid columns="1">
	        	Click "Test" to see if it's the same instance:
	        	<h:outputText id="out" value="#{st.message}" escape="false"/>
	        </h:panelGrid>
	        <p:separator/>
	        <p:commandButton value="Test" action="#{st.test}" update="out"/>
	        <p:spacer width="7"/>
	        <p:commandButton value="Clear" actionListener="#{st.reset}" update="out"/>
	    </p:panel>
    </h:form>
</h:body>
</html>


the backing bean
package com.jxee.action.test.ejb31;

import java.io.Serializable;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;

import org.apache.log4j.Logger;

import com.jxee.ejb.test.singleton.SingletonEJB;

@ManagedBean(name="st")
@RequestScoped
public class SingletonTestBean implements Serializable {

  @EJB // inject the singleton ejb
  private SingletonEJB single;
  
  // the message to build up and display. this is a bit over kill anyway.
  private static final AtomicReference<StringBuffer> message = new AtomicReference<StringBuffer>();
  
  // this backing bean instances counter
  private static final AtomicInteger beanCount = new AtomicInteger();
  
  private static final Logger log = Logger.getLogger(SingletonTestBean.class);

  
  @PostConstruct
  public void init() {
    log.debug(String.format(">>> PostConstruct: backing bean inited: %s", this));
  }
  
  public String getMessage() {
    return message.get() != null ? message.get().toString() : "";
  }

  private void buildMessge(String toappend, boolean reset) {
    StringBuffer newmsg = new StringBuffer();
    while(true) {
      StringBuffer oldmsg = message.get();
      if(!reset) {
        if(oldmsg != null) {
          newmsg.append(oldmsg);
        }
        if(toappend != null) {
          newmsg.append(toappend);
        }
      }
      if(message.compareAndSet(oldmsg, newmsg)) {
        log.debug(">>> new message appended ok");
        return;
      }
    }
  }
  
  public String test() {
    StringBuffer newmsg = new StringBuffer();
    newmsg.append("----- request arrived(sec): ")
          .append(new Date().getTime()/1000)
          .append("<br/> >>> the backing bean [")
          .append(beanCount.incrementAndGet()).append("]: ").append(this)
          .append("<br/> >>> the singleton ejb: ")
          .append(this.single.getInstance())
          .append("<br/>");
    this.buildMessge(newmsg.toString(), false);
    return null;
  }
  
  public void reset() {
    this.buildMessge(null, true);
  }
}


the singleton ejb:
package com.jxee.ejb.test.singleton;

import javax.annotation.PostConstruct;
import javax.ejb.Singleton;

import org.apache.log4j.Logger;

@Singleton
public class SingletonEJB {
  
  private static final Logger log = Logger.getLogger(SingletonEJB.class);
  
  @PostConstruct
  public void init() {
    log.debug(">>> PostConstruct: SingletonEJB");
  }
  
  public String getInstance() {
    return this.toString();
  }
}



Now take a look at concurrency control of singleton session beans.

The the container makes sure that singleton session bean can only instantiate once. But the @Singleton does not say anything about concurrency control. JEE 6 has annotations to address this issue: @ConcurrencyManagement and @Lock.

@ConcurrencyManagement applies to the singleton class and has too options ConcurrencyManagementType.CONTAINER(default) and ConcurrencyManagementType.BEAN.

When using the default, annotation @Lock(LockType.READ | LockType.WRITE) can be used to further control the access levels of shared data.

package com.jxee.ejb.test.singleton;

import javax.annotation.PostConstruct;
import javax.ejb.ConcurrencyManagement;
import javax.ejb.ConcurrencyManagementType;
import javax.ejb.Lock;
import javax.ejb.LockType;
import javax.ejb.Singleton;

import org.apache.log4j.Logger;


/**
 * Test @Singleton session ejb.
 * 
 * ConcurrencyManagementType.CONTAINER is the default concurrency control.
 * Under container managed concurrency control, @Lock(LockType) can be used
 * to define READ/WRITE access types.
 * 
 * Alternative is ConcurrencyManagementType.BEAN. With this option, the bean
 * developer has to provide control over concurrent accesses to shared data.
 * Therefore, you can use keyword "synchronized" etc, and i guess, the classes
 * in package "java.util.concurrent".
 */
@ConcurrencyManagement(ConcurrencyManagementType.CONTAINER) // the default
@Singleton
public class SingletonEJB {
  
  private static final Logger log = Logger.getLogger(SingletonEJB.class);
  
  private static Integer counter = new Integer(1);
  
  @PostConstruct
  public void init() {
    log.debug(">>> PostConstruct: SingletonEJB inited");
  }
  
  /**
   * LockType.READ: For read-only operations. Allows simultaneous access 
   * to methods designated as READ, as long as no WRITE lock is held. 
   */
  @Lock(LockType.READ)
  public Integer getCounter() {
    return counter;
  }

  /**
   * LockType.WRITE: For exclusive access to the bean instance. 
   * A WRITE lock can only be acquired when no other method with 
   * either a READ or WRITE lock is currently held. 
   */
  @Lock(LockType.WRITE)
  public void setCounter(Integer c) {
    counter = c;
  }
  
  /**
   * a convenience method
   */
  @Lock(LockType.WRITE)
  public void increment() {
    counter++;
  }
  
  public String getInstance() {
    return this.toString();
  }
}



结论:

We can see from the result that the container instantiated different backing bean instance for each request, but the injected singleton ejb was always the same instance. So we can say that the @Singleton does its trick.

The singleton EJB has all the ejb services available. It can be used to maintain application wide state, such as caching application static data, i suppose?

你可能感兴趣的:(EJB3.1,@Singleton)