Per accedere da Java ai Resource Adapter, l'architettura JCA mette a disposizioni delle API standard denominate CCI (Common Client Interface). In questo articolo vedremo un'implementazione pratica delle API CCI, presentando il MokaConnnector, un resource adapter che si connette ad una risorsa EIS rappresentata da un file testuale.
Le classi CCI del MokaConnectorIl MokaConnector espone un'interfaccia CCI compliant. Implementando quindi le interfacce CCI evidenziate dalla tabella seguente, saremo in grado di permettere l'accesso medianti API CCI al MokaConnector.
Figura 1: (clicca sull'immagine per ingrandirla)
La classe MokaCciConnectionFactoryLa classe MokaCciConnectionFactory è il factory che permette la creazione delle connessioni. Oltre ad implementare l'interfaccia CCI javax.resource.cci.ConnectionFactory deve anche implementare le interfacce java.io.Serializable (per essere serializzabile) e javax.resource.Referenceable (per supportare la registrazione presso un contesto JNDI). La classe dell'esempio MokaCciConnectionFactory ha quindi la seguente firma :
public class MokaCciConnectionFactory implements javax.resource.cci.ConnectionFactory, javax.resource.Referenceable, java.io.Serializable {
Il costruttore della classe MokaCciConnectionFactory riceve in ingresso due oggetti appartenenti al system contract di Connection Management [JMS1]: l'oggetto managed connection factory (implementa l'interfaccia javax.resource.spi.ManagedConnectionFactory) e l'oggetto ConnectionManager (implementa l'interfaccia javax.resource.spi.ConnectionManager)
Un componente J2EE per ottenere la connessione con l'EIS deve invocare il metodo getConnection() dell'oggetto MokaCciConnectionFactory. Il resource adapter demanda la responsabilità della gestione del connetion pool all'Application Server. Il metodo getConnection invoca quindi a sua volta il metodo allocateConnection() del ConnectionManager per ottenere una connessione prelevata dal Connection pool dell'Application Server. Nel caso del MokaConnector il metodo getConnection() si aspetta in ingresso un oggetto di classe MokaConnectionRequestInfo in cui vengono specificati username e password per potere accedere al file:
public Connection getConnection(ConnectionSpec properties) throws
ResourceException {
ConnectionRequestInfo info = new MokaConnectionRequestInfo(…);
return (javax.resource.cci.Connection) this.cm.allocateConnection(
this.managedConnectionFactory, info);
}
La classe MokaCciConnectionE' il riferimento alla connessione con il sottosistema EIS. public class MokaCciConnection implements javax.resource.cci.Connection {
Ad ogni oggetto javax.resource.cci.Connection è associato un oggetto javax.resource.spi.ManagedConnection (system contract di Connection Management) che gestisce direttamente la risorsa EIS.
I metodi principali di questa classe permettono al componente J2EE (il JCA client) le seguenti operazioni :
createInteraction(): crea un oggetto Interaction per permettere di interagire
con la risorsa EIS public Interaction createInteraction() throws
ResourceException {
return new MokaCciInteraction(this);
}
getLocalTransaction(): crea un oggetto per gestire in modo applicativo le
transazioni con l'EIS
public LocalTransaction getLocalTransaction()throws ResourceException {
throw new
ResourceException("#MokaConnector#: NO Trx management for file system" +
" !");
}
close(): permette di chiudere la connessione conl'EIS public void close() throws ResourceException { if (this.managedConnection != null) { this.managedConnection.close(); } }
La classe MokaCciConnectionMetaData Implementando l'interfaccia javax.resource.cci.ConnectionMetaData è possibile specificare i dati descrittivi (metadata) della connessione atttiva. Nell'esempio del MokaConnector i metadata sono rappresentati da nome e versione del ResourceAdapter e dalla user name dell'utente collegato alla risorsa EIS.
public class MokaCciConnectionMetaData implements
javax.resource.cci.ConnectionMetaData {
public MokaCciConnectionMetaData() {
}
public String getEISProductName() {
return MokaCciResourceAdapterMetaData.MOKA_RA_NAME;
}
public String getEISProductVersion() {
return MokaCciResourceAdapterMetaData.MOKA_RA_VERSION;
}
public String getUserName() {
return "anonymous user";
}
}
La classe MokaCciConnectionSpec L'oggetto ConnectionSpec permette di specificare le proprietà specifiche delle risorsa EIS al fine di ottenere la connessione in modo appropriato. Nel caso del MokaConnector tale classe permette di specificare la user name e la password nella richiesta di connessione all'EIS:
public class MokaCciConnectionSpec implements
javax.resource.cci.ConnectionSpec {
private String user;
private String password;
public MokaCciConnectionSpec(String user, String password) {
this.user = user;
this.password = password;
}
public String getUser() {
return user;
}
public String getPassword() {
return password;
}
}
La classe MokaCciResourceAdapterMetaData Questa classe fornisce informazioni sulle caratteristiche del Resource Adapter. E' ottenibile invocando il metodo getMetaData sull'oggetto ConnectionFactory. Da tale classe è possibile ottenere informazioni puramente descrittive come:
il nome del resource adapter public String getAdapterName(){return this.MOKARANAME;}
la descrizione del connettore public String getAdapterShortDescription(){return "MokaConnector: a simple RA to access system files";}
il nome del produttore public String getAdapterVendorName(){return "Mokabyte INC";}
la versione public String getAdapterVersion(){return this.MOKARAVERSION;}
la versione JCA supportata public String getSpecVersion(){return "SUN JCA 1.0";}
ma anche informazioni utili per l'operatività e l'utilizzo da parte del client J2EE come:
il supporto delle transazioni locali (il MokaConnector non implementa l'interfaccia LocalTransaction e quindi deve restituire false) public boolean supportsLocalTransactionDemarcation(){return false;}
le classi utilizzabili per interagire con la risorsa EIS public java.lang.String[] getInteractionSpecsSupported(){ String[] res = { it.mokabyte.mokajca.ra.MokaCciInteractionSpec.class.getName() }; return res; }
il supporto per l'interazione mediante il metodo Record execute(InteractionSpec ispec, Record input) public boolean supportsExecuteWithInputRecordOnly(){return true;}
l supporto per l'interazione mediante il metodo boolean execute(InteractionSpec ispec, Record input, Record output) public boolean supportsExecuteWithInputAndOutputRecord(){return false;}
La classe MokaCciInteractionSpec Contiene le proprietà necessarie per interagire con la risorsa EIS ed è utilizzata dall'oggetto Interaction. Come il ConnectionSpec, anche l'InteractionSpec è di fatto un Java Bean. La classe MokaCciInteractionSpec incapsula le informazioni relative all'operazione che si vuole eseguire sul file (lettura/scrittura) ed il numero di righe del file nel caso di lettura.
public class MokaCciInteractionSpec implements InteractionSpec,
java.io.Serializable {
private String functionName;
private int numOfLines;
<< metodi get e set >>
La classe MokaCciInteraction Un ogetto MokaCciInteraction viene costruito a partire dall'oggetto MokaCciConnection e utilizza la classe MokaCciInteractionSpec per indicare l'operazione da eseguire ed i relativi parametri.
public class MokaCciInteraction implements Interaction {
// riferimento all'oggetto connection private javax.resource.cci.Connection
// connection;
// Costruttore public MokaCciInteraction(javax.resource.cci.Connection con) {
this.connection = con;
}
La classe mette a disposzione il metodo execute per interagire con la risorsa EIS. Nel caso del MokaConnector il metodo boolean execute (InteractionSpec ispec, Record input, Record output) non è supportato.
public boolean execute(InteractionSpec ispec, Record input,
Record output) throws ResourceException {
throw new
ResourceException("# boolean execute (InteractionSpec ispec,Record " +
"in,Record output) #NOT# SUPPORTED by MokaConnector" +
" #");
}
Si noti come coerentemente a questa scelta il metodo supportsExecuteWithInputAndOutputRecord della classe MokaCciResourceAdapterMetaData ritorni false.
Il MokaConnector permette di interagire con la risorsa EIS con il solo metodo Record execute (InteractionSpec ispec, Record input). Si noti anche in questo caso la coerente implementazione del metodo supportsExecuteWithInputRecordOnly() della classe MokaCciResourceAdapterMetaData che ritorna true. Risulta quindi fondamentale capire come questo metodo consente di interagire con il file.
public Record execute(InteractionSpec ispec,
Record input) throws ResourceException {
if(ispec == null || (!(ispec instanceof MokaCciInteractionSpec))) {
throw new ResourceException(…);
}
String procName = ((MokaCciInteractionSpec)ispec).getFunctionName();
int numLines = ((MokaCciInteractionSpec)ispec).getNumOfLines();
IndexedRecord output = new MokaCciIndexedRecord();
return this.exec(procName, numLines, input, output);
}
Una volta controllata la coerenza dei parametri d'ingresso, vengono letti i valori specifici dell'interazione EIS e viene invocato il metodo privato exec private Record exec(String procName,int numLines, Record input,Record output)throws ResourceException{
La prima operazione eseguita è quella di ricavare la risorsa EIS passando dal reference CCI della connessione all'oggetto ManagedConnection (la connessione a livello SPI); sulla connessione viene poi invocato il metodo getFile
La classe MokaCciIndexedRecord E' la classe che rappresenta i dati di input ed output del MokaConnector.
public class MokaCciIndexedRecord implements javax.resource.cci.IndexedRecord {
private String recordName;
private String description;
private Vector indexedRecord;
<< metodi get, set e di wrapping del Vector >>
La classe MokaCciRecord E' la classe factory degli oggetti record di classe MokaCciIndexedRecord.
public class MokaCciRecordFactory implements javax.resource.cci.RecordFactory {
public MokaCciRecordFactory() {
}
public MappedRecord createMappedRecord(String recordName) throws
ResourceException {
throw new ResourceException("MappedRecord not supported");
}
public IndexedRecord createIndexedRecord(String recordName) throws
ResourceException {
return new MokaCciIndexedRecord(recordName);
}
}
L'EJB TesterBean e l'utilizzo del MokaConnectorAndiamo ora ad analizzare un esempio d'uso delle interfacce implementate. Nello scenario in esame, un componente J2EE (un EJB Stateful) accedererà al MokaConnector attraverso le API CCI.
Figura 2
La home interface La home interface espone un unico metodo di create:
public interface TesterHome extends EJBHome {
public Tester create() throws RemoteException, CreateException;
}
La remote interface
La remote interface pubblica i metodi necessari per utilizzare il connettore. Il metodo performRead permette di leggere un numero arbitrario di righe del file (-1 per leggere l'intero file). Il metodo performWrite() permette di scrivere il contenuto dell'oggetto ListModel sul file(una semplice classe Wrapper di una collection Java).
public interface Tester extends EJBObject {
public void performWrite(ListModel vals)throws RemoteException,
BusinessException;
public ListModel performRead(int num) throws RemoteException,
BusinessException;
}
Figura 3
L'EJB TesterBean All'interno del metodo ejbCreate si inizializzano le proprietà connectionFactory econnection dell'EJB
public class TesterBean implements SessionBean {
// il nome JNDI del MokaConnector private static final String JNDI_NAME_EIS =
// "java:comp/env/ra/CCI_MOKA_EIS";
// username per utilizzare la risorsa EIS private static final String
// JNDI_NAME_USR = "java:comp/env/username";
// password per accedere alla risorsa EIS private static final String
// JNDI_NAME_PWD = "java:comp/env/password";
// Il ConnectionFactory private ConnectionFactory connectionFactory= null;
// La Connessione con la risorsa EIS private Connection connection= null;
public void ejbCreate() throws CreateException {
log.debug("TesterBean.ejbCreate : called ...");
try {
// Recupero del ConnectionFactory this.connectionFactory =
// this.getCCIConnectionFactory();
// si ottiene la connessione alla risorsa EIS this.connection = this.getCCIConnection(this.connectionFactory);
} catch(NamingException ne) {
throw new CreateException(ne.getMessage());
} catch(ResourceException re) {
throw new CreateException(re.getMessage());
}
}
Il metodo getCCIConnectionFactory istanzia un JNDI Context ed effettua l'opportuna lookup ed il relativo downcast del ConnectionFactory.private ConnectionFactory getCCIConnectionFactory() throws NamingException{ Context ic = new InitialContext(); ConnectionFactory cf = (ConnectionFactory) ic.lookup(this.JNDINAMEEIS); return cf; }
Il metodo getCCIConnection effettua la lookup dei parametri username e password necessari a reistanziare un oggetto MokaCciConnectionSpec. Mediante questo è possibile invocare il metodo getConnection sull'oggetto ConnectionFactory ricevuto in ingresso.
private Connection getCCIConnection(ConnectionFactory cf) throws
NamingException, ResourceException {
Context ic = new InitialContext();
String user = (String) ic.lookup(this.JNDI_NAME_USR);
St ring password = (String) ic.lookup(this.JNDI_NAME_PWD);
MokaCciConnectionSpec spec ;
spec = new MokaCciConnectionSpec(user, password);
Connection con = cf.getConnection(spec);
return con;
}
I metodi ejbRemove ed ejbPassivate provvedono alla chiusura della connessione invocando il metodo closeCCIConnection this.closeCCIConnection(this.connection); che di fatto si limita ad invocare il metodo close sull'oggetto connection ricevuto come paramtro d'ingresso con.close(); Nel metodo ejbActivate viene nuovamente ottenuta una connessione this.connection = this.getCCIConnection(this.connectionFactory);
Vediamo ora i metodi che permettono di leggere e scrivere sulla risorsa EIS. Il metodo performRead permette di leggere dalla risorsa EIS
public ListModel performRead(int num) throws BusinessException {
ListModel result = new ListModel();
try {
si crea l'oggetto Interaction Interaction ix =
this.connection.createInteraction(); Si specifica l'intenzione di
effettuare un'operazione di lettura MokaCciInteractionSpec iSpec = new
MokaCciInteractionSpec(); iSpec.setFunctionName("READ"); specificando il
numero di righe che si è interessati a leggere
iSpec.setNumOfLines(num);
si crea un oggetto RecordFactory RecordFactory rf =
this.connectionFactory.getRecordFactory();
un oggetto IndexedRecord IndexedRecord iRec =
rf.createIndexedRecord("InputRecord");
si invoca il metodo execute memorizzando
l'opportuno output Record oRec = ix.execute(iSpec, iRec); si cicla sul Record di
output Iterator iterator = ((IndexedRecord)oRec).iterator(); int count = 0;
while(iterator.hasNext()) { Object obj = iterator.next(); ++count; popolando
il ListModel da restituire result.add("" + obj); } si ritorna il ListModel
contenente il risultato dell'interazione
con la risorsa EIS return result;
}
Il metodo performWrite permette invece di scrivere sulla risorsa EIS
public void performWrite(ListModel vals) throws BusinessException {
try {
Interaction ix = this.connection.createInteraction();
MokaCciInteractionSpec iSpec = new MokaCciInteractionSpec();
log.debug("setFunctionName to WRITE ...");
si specifica l'intenzione di effettuare un'operazione di lettura
Il client EJB E' un'applicazione Java standalone che invoca i metodi dell'EJB per testare il funzionamento del Mokaconnector. Una volta ottenuto il reference della remote interface
Context ctx = new InitialContext();
Object ref = ctx.lookup(EJB_JNDI_NAME);
home = (TesterHome) PortableRemoteObject.narrow(ref, TesterHome.class);
obj = home.create();
si effettuano le prove di lettura invocando il metodo di business performRead
System.out.println(obj.performRead(1));
System.out.println(obj.performRead(3));
// leggo tutto il file e di scrittura mediante
// l'invocazione del metodo di business performWrite
// System.out.println(obj.performRead(-1)); String values[] = {
// "uno","due","tre" }; obj.performWrite(new ListModel(values)); alla fine del
// test si rimuove l'EJB obj.remove();
ConclusioniIn questo articolo è stato analizzato un'esempio di implementazione delle API CCI per il MokaConnector. Il mese prossimo si inizieranno a studiare i sytem contracts e la relativa implementazione nel MokaConnector. Bibliografia e riferimenti [JCA1] G.Morello - S.Rossini: "Java Connector Architecture", Mokabyte N.66 - Settembre 2002[JCA2] G.Morello - S.Rossini: "Java Connector Architecture", Mokabyte N.67 - Ottobre 2002 [JCA] http://java.sun.com/j2ee/connector/[SPEC] J2EE Connector Architecture Specification, http://java.sun.com/j2ee/download.html#connectorspec [CCI1] Beth Stearns: Using the J2EETM Connector Architecture Common Client Interface, http://java.sun.com,Aprile 2001[BSTW] David Marks: J2EE Connector Architecture Brings Business Systems to the web, http://developer.java.sun.com/developer/technicalArticles/J2EE/connectorclient/resourceadapter.html[JW2] Dirk Reinshagen: Connect the enterprise with the JCA, Part 2,JavaWorld Febbraio 2002[JROD] Jennifer Rodoni: The Java 2 Enterprise Edition Connector Architecture's Resource Adapter, http://developer.java.sun.com/developer/technicalArticles/J2EE/connectorclient/resourceadapter.html[JTUT] J2EE Tutorial, http://java.sun.com/j2ee/tutorial/download.html : esample of black box resource adapter for J2EE reference implementation.[JBOSS] http://www.jboss.org
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