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
|