Some thoughts on Java Platform
Header image

Singleton Session Beans

Posted by raphaufrj in OCE-EJBD 6

Uma das novidades trazidas pela spec da JEE6 é o novo session bean: Singleton. Para quem conhece o pattern, se não o mais conhecido da Gof, eu não discutirei aqui, mas colocarei na referência. Basicamente, a idéia é que suportada pelo container session beans de única instância.

Exemplo:


@javax.ejb.Singleton

@javax.ejb.Startup

public class MySingletonBean implements MySingletonLocalBusiness{..}

A anotação @Singleton identifica que o session bean é Singleton. Já a anotação @Startup indica que singleton será criado tão logo o container for iniciado. Caso não seja especificado, o container carregará o bean apenas quando ele for referenciado (lazy initialization).

Por padrão, é possível também especificar para esse bean o método de PostConstruct:


@javax.ejb.PostConstruct

public void applicationStartLifecycleMethod() throws Exception{...}

Esse método será chamado, tão logo o bean ser inicializado pelo container (chamando o construtor sem argumento) e injetados as propriedades do bean.

Concorrência

Diferente dos outros beans, o Singleton Session Bean por ter uma instância só, pode ser compartilhado por múltiplos clientes. Logo, existe uma preocupação com a concorrência. Para isso, o container possui três tipos de gerência de concorrência:

  1. @javax.ejb.ConcurrencyManagement(javax.ejb.ConcurrencyManagementType.CONTAINER) – Gerenciado pelo Container
  2. @javax.ejb.ConcurrencyManagement(javax.ejb.ConcurrencyManagementType.BEAN) – A critério do Bean Provider
  3. @javax.ejb.ConcurrencyManagement(javax.ejb.ConcurrencyManagementType.CONCURRENCY_NOT_SUPPORTED)

A concorrência por CONTAINER é a mais importante, e é a padrão caso não seja especificado. Em conjunto com a @javax.ejb.Lock, é possível definir tais características.

Exemplo:

@javax.ejb.ConcurrencyManagement(javax.ejb.ConcurrencyManagementType.CONTAINER)

public class MyBean{

@javax.ejb.Lock(javax.ejb.LockType.READ)

@javax.ejb.AccessTimeout(timeout=15,unit=java.util.concurrent.TimeUnit.SECONDS)

public String concurrentReadOnlyMethod(){...}

}

@javax.ejb.Lock(javax.ejb.LockType.WRITE)

public void allowOnlyOneWriteAtATimeMethod(String stringToSet){...}

O Lock pode ser de dois tipos: READ ou WRITE. O lock do tipo WRITE bloqueia a classe para acesso de apenas uma Thread por vez, o que significa que todos as outras threads deverão esperar até o lock tornar-se disponível para acessar a classe.

É possível especificar timeouts de espera do método com o @AcessTimeout, no exemplo uma thread ficará aguardando o método por 15 segundos. Caso não seja suficiente retornará um javax.ejb.ConcurrentAccessTimeoutException. Isso evita que a thread fique esperando pelo bean por tempo indeterminado.

Pegadinha quanto a Lock. Considere o seguinte bean:

@Singleton
@LocalBean
@Lock(LockType.WRITE)
public class CrazySingletonBean {
 String s1 = "x";
 String s2 = "y";

 public void setStrings(){
 s1 = "x"; s2 = "y";
 }

 public void clearStrings(){
 s1 = ""; s2 = "";
 }

 @Lock(LockType.READ)
 public String getValue(){
 return s1+" "+s2;
 }
}
<pre>

Os métodos setStrings() e clearStrings() são chamados por outro bean em loop, enquanto o getValue() é chamado por alguns outros beans periodicamente.
Quais dos seguintes valores retornados por getValue() são válidos?

a) “x y” or ” “
b) “x y”, ” “, “x “, or ” Y”
c) “x y”
d) alguns clientes podem retornar “x y” enquanto outros podem retornar ” “.

A opção correta é a em negrito. A razão para isso é a questão do Lock. Repare que no topo da classe definimos que os métodos tem Lock do tipo WRITE o que significa que quando um cliente acessa um método desse tipo, nenhuma outra thread de outro cliente poderá acessar esse ou outro método do bean ao mesmo tempo. A segunda coisa a se reparar é que apesar da classe ter declarado Lock do tipo WRITE o método getValue() tem o lock do tipo READ, o que significa que enquanto um cliente chama esse método, outras thread poderiam ocorrer em outros métodos que fossem também do tipo READ, no caso dessa classe, múltiplas threads podem acessar getValue() simultaneamente.

Logo, a opção A é a correta.

 

A principal pegadinha que vi até o momento é relativa ao lançamento de uma runtime exception dentro de um business method de um singleton. Exemplo:

 


@Singleton

public class CountryCodeBean {

private Map<String, String> ccMap;

 @PostConstruct     public void initializeCountryData(){      ...     }

@Lock(LockType.READ)

public String getCode(String country){

if(country == null)

throw new EJBException("Bad Input");

return ccMap.get(country);

}
}

Vamos imaginar que existe um cliente válido que tenta chamar getCode(null). Todas as vezes que ele chamar com null, ele receberá uma EJBException que é uma runtime exception. Agora pergunta, quantas instâncias desse Singleton são criadas. De acordo com os conhecimento sobre tratamento de exceções em Enterprise JavaBeans sabemos que uma runtime exception causa a destruição da instância do bean, mas para o caso particular de Singleton Session Beans, lançar qualquer exception dentro do business method não causará a destruição da instância. Ou seja, nesse caso em especial, quantas vezes forem chamadas o método getCode(null), será criada e mantida apenas 1 instância do Singleton.

 

Por hora isso resume o tema Singleton Session Bean. Próximo post algumas pegadinhas de prova relacionadas. Até breve!

Referências:

Singleton pattern wikipedia - http://pt.wikipedia.org/wiki/Singleton

You can follow any responses to this entry through the RSS 2.0 You can leave a response, or trackback.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>