14.     The Service Locator Pattern
J2EE규약은 다른 리소스나 서비스들에 대한 접근을JNDI에 위임한다. J2EE호환 서버는 이런한 리소스나 서비스들을JNDI서버에 바인드함으로써 클라이언트가 네트워크의 어디에도JNDI lookup프로세스를 통해 리소스나 서비스들을lookup할수 있도록 한다.이러한 리소스나 서비스의 종류는EJBHome, DateSource, JMS ConnectionFactory, JMS Topic/Queue등이 있다.이러한 요소들의 객체는JNDI API를 사용하여 얻어진다. JNDI룩업을 수행하여 어떻게 벤더에서 다른 벤터로 변경하는 것이 가능할까? JNDI룩업 수행의 반복은 시스템의 부하를 줄수 있다.
JNDI로부터 서비스를 호출해야 할 때 매번 사용자가 적절한JNDI서버에 연결하고, JNDI환경 정보를 알아내어 서비스를 찾는다.필요한 것은JNDI로부터 다양한 서비스를 생성 하고 가져올수 있다는 단일점이다.여기에서Service Locator패턴이 사용된다.
Service Locator패턴은 단순하게 사용할 수 있는 인터페이스로 모든JNDI룩업방식을 추상화 한다.사용자가 특정한EJBJDBC연결을 요청할 때 데이터 소스에 연결하려고 처음 생성했던JDBC커넥션 객체를 재사용하고, EJBHome인터페이스를 케싱함으로써JNDI룩업의 호출 횟수를 최소화한다.
 
구조

사용자 삽입 이미지

 
의도
캐싱을 이용한 객체 재사용과JNDI룩업 프로세스을 줄여주고 퍼포먼스을 비약적으로 향상시킨다.
 
예제소스
package common;
 
import java.sql.*;
import javax.sql.DataSource;
import java.util.Hashtable;
import javax.naming.*;
import javax.ejb.*;
import javax.rmi.PortableRemoteObject;
import common.counter.CounterHome;
import entity.booking.BookingLocalHome;
import entity.city.CityLocalHome;
import entity.flight.FlightLocalHome;
import entity.customer.CustomerLocalHome;
import entity.hotel.HotelLocalHome;
import javax.jms.*;
import facade.customer.*;
import facade.citybreak.*;
import facade.bookingmsg.*;
 
public class ServiceLocator{
 private static ServiceLocator serviceLocatorRef                                           = null;
 private static Hashtable     ejbHomeCache                                                              = null;
 private static Hashtable     dataSourceCache                                                           = null;
 private static Hashtable                            queueConnectionFactoryCache  = null;
 private static Hashtable                            queueCache                                                                                      = null;
 private static Hashtable                            ejbLocalHomeCache                                     = null;
 
 public static final int COUNTER                                 = 0;
 public static final int BOOKING                                                = 1;
 public static final int FLIGHT                                                    = 3;
 public static final int HOTEL                                                      = 5;
 ...
 
 //요청된JNDI이름
 private static final String COUNTER_JNDINAME="ejb/Counter";
 private static final String BOOKING_JNDINAME="java:comp/env/ejb/Booking";
 private static final String FLIGHT_JNDINAME="java:comp/env/ejb/Flight";
 private static final String HOTEL_JNDINAME="java:comp/env/ejb/Hotel";
 ...
 
 // EJB Home클래스들
 private static final Class COUNTERCLASSREF                                        = CounterHome.class;
 private static final Class BOOKINGCLASSREF                                         = BookingLocalHome.class;
 private static final Class FLIGHTCLASSREF                                                               = FlightLocalHome.class;
 private static final Class HOTELCLASSREF                                                                = HotelLocalHome.class;
 ...
 
 //싱글톤생성자
 static {
                 serviceLocatorRef = new ServiceLocator();
 }
 
 public ServiceLocator(){
                 ejbHomeCache                                                               = new Hashtable();
                 ejbLocalHomeCache                                     = new Hashtable();
   dataSourceCache                                                                      = new Hashtable();
                 queueConnectionFactoryCache  = new Hashtable();
                 queueCache                                                                                      = new Hashtable();
 }
 
 public static ServiceLocator getInstance(){
   if (serviceLocatorRef == null) {
                    serviceLocatorRef = new ServiceLocator();           
   }
                 return serviceLocatorRef;
 }
 
 static private String getServiceName(int pServiceId)
   throws ServiceLocatorException{
   String serviceName = null;
 
   switch (pServiceId){
     case COUNTER:
                                                    serviceName = COUNTER_JNDINAME;
                        break;
     case BOOKING:
                                                    serviceName = BOOKING_JNDINAME;
                                                    break;
     case FLIGHT:
                                                    serviceName = FLIGHT_JNDINAME;
                                                    break;
     case HOTEL:
                                                    serviceName = HOTEL_JNDINAME;
                                                    break;
                  default:
                                                    throw new ServiceLocatorException("Unable to locate the service requested in " +
                                                                                                                                                                                  "ServiceLocator.getServiceName() method. ");
   }
 
   return serviceName;
 }
 
 static private Class getEJBHomeRef(int pServiceId)
   throws ServiceLocatorException{
   Class homeRef = null;
 
   switch (pServiceId){
     case COUNTER: 
                                                    homeRef = COUNTERCLASSREF;
                        break;
     case BOOKING:
                                                    homeRef = BOOKINGCLASSREF;
                                                    break;
     case FLIGHT:
                                                    homeRef = FLIGHTCLASSREF;
                                                    break;
     case HOTEL:
                                                    homeRef = HOTELCLASSREF;
                                                    break;
     default:
                                                    throw new ServiceLocatorException("Unable to locate the service requested in " +
                                                                                                                                                                                     "ServiceLocator.getEJBHomeRef() method. ");
   }
 
   return homeRef;
 }
 
 public EJBHome getEJBHome(int pServiceId)
   throws ServiceLocatorException{
   //요청된서비스에eogks JNDI이름을찾으려시도한다.
   String serviceName = getServiceName(pServiceId);
   EJBHome ejbHome   = null;
 
   try{
     // EJBLocal Home인터페이스를캐시에서찾을수있는지체크한다.
     if (ejbHomeCache.containsKey(serviceName)){
                                  ejbHome = (EJBHome) ejbHomeCache.get(serviceName);
                                  return ejbHome;
     }
     else{
       //캐시에서찾을수없다면JNDI에서룩업하고캐시에저장한다.
                                  Context ctx = new InitialContext();
       Object jndiRef    = ctx.lookup(serviceName);
       Object portableObj =
         PortableRemoteObject.narrow(jndiRef, getEJBHomeRef(pServiceId));
       ejbHome = (EJBHome) portableObj;
       ejbHomeCache.put(serviceName, ejbHome);
                                  return ejbHome;
     }
   }
   catch(NamingException e){
     throw new ServiceLocatorException("Naming exception error in ServiceLocator.getEJBHome()" + e);
   }
   catch(Exception e){
     throw new ServiceLocatorException("General exception in ServiceLocator.getEJBHome" + e);
   }
 }
 
 public EJBLocalHome getEJBLocalHome(int pServiceId)
   throws ServiceLocatorException{
   String serviceName = getServiceName(pServiceId);
   EJBLocalHome ejbLocalHome   = null;
 
   try{
     if (ejbLocalHomeCache.containsKey(serviceName)){
                                  ejbLocalHome = (EJBLocalHome) ejbLocalHomeCache.get(serviceName);
                                  return ejbLocalHome;
     }
     else{
                                  Context ctx = new InitialContext();
       Object jndiRef    = ctx.lookup(serviceName);
       ejbLocalHome = (EJBLocalHome) jndiRef;
       ejbLocalHomeCache.put(serviceName, ejbLocalHome);
                                  return ejbLocalHome;
     }
   }
   catch(NamingException e){
     throw new ServiceLocatorException("Naming exception error in ServiceLocator.getEJBLocalHome()" + e);
   }
   catch(Exception e){
     throw new ServiceLocatorException("General exception in ServiceLocator.getEJBLocalHome" + e);
   }
 }
 
 
 public java.sql.Connection getDBConn(int pServiceId)
   throws ServiceLocatorException{
   String    serviceName = getServiceName(pServiceId);
   java.sql.Connection conn       = null;
 
   try{
     if (dataSourceCache.containsKey(serviceName)){
                 DataSource ds = (DataSource) dataSourceCache.get(serviceName);
                 conn = ((DataSource)ds).getConnection();
                 return conn;
     }
     else{
                 Context ctx = new InitialContext();
       DataSource newDataSource = (DataSource) ctx.lookup(serviceName);
 
                 dataSourceCache.put(serviceName, newDataSource);
 
                 conn = newDataSource.getConnection();
                 return conn;
     }
   }
   catch(SQLException e){
     throw new ServiceLocatorException("A SQL error has occurred in ServiceLocator.getDBConn()" + e);
   }
   catch(NamingException e){
     throw new ServiceLocatorException("A JNDI Naming exception has occurred in ServiceLocator.getDBConn()" + e);
   }
   catch(Exception e){
     throw new ServiceLocatorException("An exception has occurred in ServiceLocator.getDBConn()" + e);
   }
 }
 
 public QueueConnection getJMSQueueConn(int pServiceId) throws ServiceLocatorException {
 ...
 }
 
 
 public Queue getJMSQueue(int pServiceId) throws ServiceLocatorException {
 ...
 }
}

Posted by 영웅기삼
,