MokaByte 67 - 8bre 2002 
Il Pattern Service Locator
di

S. Rossini
L. Dozio

Introduzione
La localizzazione dei servizi e la relativa creazione sono operazioni complesse che possono richiedere accessi a risorse di rete.

 

A cosa serve
Il pattern Service Locator gestisce le operazioni di localizzazione e creazione di Business
Objects occupandosi di :

  • "nascondere" la complessità delle operazioni di ricerca (lookup) dei servizi
  • fornire una modalità uniforme di lookup e creazione dei servizi
  • isolare le eventuali operazioni di lookup "vendor dependent"
  • nascondere eventuali problematiche di networking
  • gestire eventuali necessità di caching

concentrando così la gestione delle problematiche in un singolo punto.

Motivazione
Il pattern garantisce una modalità uniforme e semplificata di localizzazione di risorse.
Spiegazione del funzionamento
La classe ServiceLocator effettua le operazioni che permettono l'inizializzazione del contesto JNDI e la localizzazione dell'oggetto Factory [GOF] d'interesse.
Di seguito si riporta il codice di un client che ricerca un EJB mediante le API JNDI senza utilizzare il pattern Service Locator :

import javax.naming.*;
import javax.rmi.PortableRemoteObject;
import it.mokabyte.pattern.ejb.entity.AccountHome;

public NoServiceLocatorTestClient{
. . .

public void doTest(){
. . .
try {
Context ctx = new InitialContext();
Object obj = ctx.lookup("MokaAccount");
AccountHome accountHome =
(AccountHome)PortableRemoteObject.narrow(obj, AccountHome.class);
} catch(NamingException ne) { . . . }
. . .
}
. . .
}

Per confronto si propone lo stesso client che effettua la medesima ricerca mediante il
ServiceLocator:

import it.mokabyte.pattern.delegate.MyServiceLocator;
import it.mokabyte.pattern.exception.ServiceLocationException;

public ServiceLocatorTestClient{
. . .

public void doTest(){
. . .
try {
AccountHome home =
(AccountHome)ServiceLocator.getInstance().
getHome(<<SERVICEID>>);
} catch(ServiceLocationException sle) { . . . }
. . .
}
. . .
}

In questo caso l'implementazione del Service Locator è un Singleton [GOF] su cui si invoca un metodo (getHome) specificando come parametro di input (SERVICEID) il servizio da ricercare (algoritmo di tipo "type-checked").
L'implementazione del ServiceLocator proposta, espone anche API in cui è richiesto espressamente il nome del servizio e la classe che si sta ricercando. Un approccio di questo tipo è detto "type-unchecked".

. . .
try {
AccountHome home =
(AccountHome)MyServiceLocator.getInstance().getHome(
"MokaAccount", AccountHome.class);
} catch(ServiceLocationException sle) { . . . }
. . .

A parità di funzionalità offerte, il client che utilizza il ServiceLocator non è chiamato ad effettuare le operazioni di lookup e di narrow; conseguentemente non deve importare i package java.rmi e javax.naming.

 


Struttura
Si riportano i diagrammi UML relativi al pattern.

 


Class Diagram (ServiceLocator EJBHome)

 


Class Diagram (ServiceLocator TopicConnectionFactory)

 


Sequence Diagram (EJB Home)

 


Sequence Diagram (TopicConnectionFactory)
(clicca sull'immagine per ingrandire)

 

Presentazione del codice Java
Viene ora presentato un esempio di implementazione.


Class Diagram dell'esempio proposto
(clicca sull'immagine per ingrandire)

Come prima cosa bisogna definire gli ID logici dei servizi che verranno utilizzati dal client per indicare il servizio d'interesse.

public final static int MOKA_SHOPPER_SERVICE = 0;
public final static int MOKA_ACCOUNT_SERVICE = 1;
. . .

Si deve provvedere a configurare opportunamente ogni servizio specificando la classe Home, la classe ContextFactory, il Provider URL ed il JNDI name.
Per fare questo ci si avvale di una classe Helper il cui compito è di contenere i dati di configurazione del servizio mettendo a disposizione i relativi metodi accessor.

class ServiceConfig {
private Class homeClass = null;
private String jndiName = null;
private String initialContextFactory = null;
private String providerUrl = null;
. . .
Class getHomeClass() {return this.homeClass;}
String getJndiName() {return this.jndiName;}
String getInitialContextFactory() {
       return this.initialContextFactory;
}
String getProviderUrl() {return this.providerUrl;
}

A questo punto si provvede all'effettiva configurazione dei servizi creando un array di configurazione.

/** Configuration Array */
private static final Object businessServices[] = {
/* 0 : Shopper */
new ServiceConfig(
it.mokabyte.pattern.ejb.session.stateful.ShopperHome.class,
<<JNDI SHOPPER>>,
"org.jnp.interfaces.NamingContextFactory",
"jnp://localhost:1099",
null,null),
/* 1 : Account */
new ServiceConfig(
it.mokabyte.pattern.ejb.entity.AccountHome.class,
<<JNDI ACCOUNT>>,
"com.sun.jndi.cosnaming.CNCtxFactory",
"iiop://localhost:1050",
null,null),
. . .
};

Il parametro service individua l'oggetto ServiceConfig da utilizzare al fine di localizzare il servizio d'interesse.

public EJBHome getHome(int service) throws ServiceLocationException {
EJBHome home = null;
try {
// Look up using the service name from defined constant
ServiceConfig config=
(ServiceConfig)MyServiceLocator.businessServices[service];
Context ctx =
this.getInitialContext(config.getInitialContextFactory(),
                       config.getProviderUrl());
Object objref = ctx.lookup(config.getJndiName());

// Narrow using the EJBHome Class from defined constant
Object obj = PortableRemoteObject.narrow(
                                  objref,config.getHomeClass());

home = (EJBHome)obj;
}
catch( Exception ex ) {
ex.printStackTrace();
throw new ServiceLocationException(ex.getMessage());
}
return home;
}


Un caso d'uso
Il caso d'uso proposto prevede un programma client che richiede alla classe ServiceLocator di reperire :

  • l'EJB Account installato sull'Application Server SUN Reference Implementation [DAO] in modalità "type-checked"
    AccountHome home =(AccountHome)MyServiceLocator.getInstance().getHome(
    MyServiceLocator.MOKA_ACCOUNT_SERVICE);
  • l'EJB Shopper installato sull'Application Server JBoss[FACADE] in modalità "type- checked"
    ShopperHome home = (ShopperHome)MyServiceLocator.getInstance().getHome(
    MyServiceLocator.MOKA_SHOPPER_SERVICE);
  • l'EJB Facade installato sull'Application Server JBoss[FACADE] in modalità "type-unchecked"

    FacadeHome home = (FacadeHome)MyServiceLocator.getInstance().getHome(
    JndiNames.FACADE,FacadeHome.class);

  • il Topic Connection Factory [JMS] installato sull'Application Server JBoss in modalità "type-unchecked"

    TopicConnectionFactory home TopicConnectionFactory)
              MyServiceLocator.getInstance().
                               getTopicHome(
                               JndiNames.TOPIC_CONNECTION_FACTORY);

     


 

Allegati
L'esempio è scaricabile qui

 

Bibliografia e riferimenti
[DAO] S.Rossini, L. Dozio: "J2EE Patterns Dao", Mokabyte N.62 Aprile 2002
[FACADE] S.Rossini, L. Dozio: "J2EE Patterns Facade", Mokabyte N.64 Giugno 2002
[JMS] S.Rossini: "Java Message Service: la pratica", Mokabyte N.61, Marzo 2002
[GOF] Gamma, Helm, Johnson, Vlissides:
Design Patterns - Elements of Reusable Object-Oriented Software
[J2EE] Alur,Crupi,Malks:
Core J2EE Patterns - Best Practices and Design Strategies
[SJP] Sun Java Center J2EE Patterns:
http://developer.java.sun.com/developer/restricted/patterns/J2EEPatternsAtAGlance.html
[BPP] Sun blueprints-Design Patterns Catalog :
http://java.sun.com/blueprints/patterns/j2ee_patterns/catalog.html

 

MokaByte® è un marchio registrato da MokaByte s.r.l. 
Java®, Jini® e tutti i nomi derivati sono marchi registrati da Sun Microsystems.
Tutti i diritti riservati. E' vietata la riproduzione anche parziale.
Per comunicazioni inviare una mail a info@mokabyte.it