Service Locator
por Giovani Salvador

São inúmeros os design patterns existentes e especificamente para a arquitetura J2EE temos alguns bons patterns que ajudam a simplificar a codificação.
Neste artigo estarei abordando um dos catalogados para a arquitetura J2EE, o Service Locator. O nome deste catálogo chama-se Core J2EE Patterns (
Core J2EE Patterns
)

O problema:

Clientes de serviço EJB precisam localizar o serviço (através de chamadas lookup) utilizando algumas formas padrão da arquitetura de EJB. Estas formas envolvem a localização do objeto que implementa a interface Home do EJB, a solicitação de uma nova instância de um EJB e a chamada aos métodos de negócio. Além destes passos, se vários clientes tem este mesmos trechos de código, existe a duplicidade desta codificação, dificultando a manutenção.
Um outro problema é que se múltiplos clientes solicitam o mesmo objeto Home a performance da aplicação pode degradar uma vez que muitos recursos são consumidos na localização de um EJB.

Exemplo de uma chamada de um cliente de um EJB sem utilizar o ServiceLocator: 
 

    /* A linha abaixo inicializa um contexto para começar a fazer operações de naming.
     *parâmetros adicionais podem ser passados ao initialcontext contendo informações do ambiente.
     */
    Context ctx = new InitialContext();
  

    //a linha de código abaixo retorna um objeto que contém uma referência local ao EJB
    Object ref = ctx.lookup("SessionFacade");
    

    //Transforma a referência local do objeto ref para o tipo do EJB
    SessionFacadeHome sessionFacadeHome = 
      
(SessionFacadeHome)PortableRemoteObject.narrow(ref, SessionFacade);
  

    //alinha abaixo cria uma instância de um Session Bean de fachada
    SessionFacade sessionFacade = sessionFacadeHome.create();

 

Os passos acima geralmente estão distribuídos em vários clientes de EJB resultando nos problemas especificados anteriormente. A Solução:

Use um ServiceLocator para abstrair todos estes passos de chamadas a serviço de EJB.
Os passos para localização dos serviços EJB ficam dentro da classe ServiceLocator e os clientes apenas interagem com esta classe.
O cliente passaria a ter esta chamada: 
 

package br.com.portaljava.tutoriais.ejb;

public class 
TestServiceLocator {

  public static void 
main( String[]args ) {
    
ServiceLocator serviceLocator = ServiceLocator.getInstance();
    try 
{
      
SessionFacadeHome sessionFacadeHome =
        
(SessionFacadeHome) serviceLocator.getHome(SessionFacadeHome);
      
SessionFacade sessionFacade = sessionFacadeHome.create();
      
sessionFacade.inclui(...);
    }catch
(ServiceException ex ) {
      
System.out.println( ex.getMessage( ));
    
}
  
}
}

 Código da classe ServiceLocator (Observe que esta classe também implementa o pattern Singleton):

 

package br.com.portaljava.tutoriais.ejb;

import 
javax.ejb.*;
import 
javax.naming.*;
import 
java.util.*;
import 
javax.rmi.PortableRemoteObject;

/**

 * Implementação de exemplo do Design Pattern Service Locator. Esta classe
 * implementa um cache de interfaces home.
 
 @author Giovani Salvador
 *  
 */
public class ServiceLocator {

  /**

   * Contexto JNDI cuja instaciação será reduzida, já que o custo deste tipo
   * de operação é alto.
   */
  private InitialContext jndiContext;

  /**

   * Este ServiceLocator será um Singleton
   */
  private static ServiceLocator instance;

  /**

   * Cosntrutor privado, evitando multiplas instâncias deste objeto(Singleton).
   *
   */
  private ServiceLocator() {
    try 
{
      jndiContext = new 
InitialContext();
    catch 
(Exception exc) {
      
exc.printStackTrace();
    
}
  
}

  /**

   * Recupera a instancia do Singleton
   */
  public static ServiceLocator getInstance() {
    if (instance == null
) {
      instance = new 
ServiceLocator();
    
}
    return 
instance;
  
}

  /**

   * Recupera uma implementação da interface home. O lookup do bean é sempre
   * feito pois é remoto.
   */
  public EJBHome getHome(String jndiName) {
    Object ref = null
;
    try 
{
      
ref = jndiContext.lookup(jndiName);
      
EJBHome home = (EJBHome) PortableRemoteObject.narrow(ref,
          EJBHome.class
);
    catch 
(Exception exc) {
      
exc.printStackTrace();
    
}
    return 
(EJBHome) ref;
  
}

  /**

   * Recupera uma implementação da interface Local home. O lookup do bean só é
   * feito se não houver nenhuma referência sua no cache daEJBHomeFactory
   */
  public EJBLocalHome getLocalHome(String jndiName) {
    EJBLocalHome localHome = null
;
    try 
{
      
localHome = (EJBLocalHome) EJBHomeFactory.getInstance().lookup(
          
jndiName);
    catch 
(Exception exc) {
      
exc.printStackTrace();
    
}
    return 
localHome;
  
}
}


Preste atenção a dois métodos na classe ServiceLocator: O getHome e getLocalHome.
O getHome retorna uma referência a uma home de um ejb remoto ao passo que o getLocalHome retorna uma referência a uma home local. Além disso, o método getLocalHome está usando outro pattern para manter o cache de referências a home´s locais, o design pattern EJBHomeFactory, do livro EJB Design Patterns de Floyd Marinescu.
Porque não mantemos cache de referências a home´s remotas também? Bem, em um ambiente distribuído isto pode não ser uma boa prática pois um componente pode estar atendendo em uma máquina e de repente pode atender de outra máquina caso a primeira tenha caído. Daí a referência remota não existe mais. Entraremos em detalhes deste pattern em outro artigo.
É importante observar novamente que o design pattern Service Locator, além de diminiuir a complexidade do código cliente, poupa recursos do lado servidor, aumentando a performance da aplicação.