Le classi CCI del MokaConnector
Il 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 MokaCciConnectionFactory
La
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)
public
MokaCciConnectionFactory(ManagedConnectionFactory mcf,
ConnectionManager
cm){
this.managedConnectionFactory = mcf;
this.connectionManager = cm;
}
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 MokaCciConnection
E' 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.
//
riferimento all'oggetto managed connection
private MokaManagedConnection managedConnection;
// costruttore
public MokaCciConnection (MokaManagedConnection mc)
{
this.managedConnection = mc;
}
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.MOKA_RA_NAME;}
- 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.MOKA_RA_VERSION;}
- 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
java.io.File file = ((MokaCciConnection)this.connection).
getManagedConnection().getFile();
è
necessario poi inizializzare opportunamente il Record
output per contenere il risultato dell'interazione
IndexedRecord oRec = null;
if(output instanceof IndexedRecord) {
oRec = (IndexedRecord)output;
}
else {
oRec = new MokaCciIndexedRecord();
}
nel
caso in cui si richieda un'operazione di lettura del
file la stringa procName conterrà il valore "READ"
if(procName.equals("READ"))
{
se
il file non esiste, viene lanciata una ResourceException
if(!file.exists())
throw new ResourceException(
);
altrimenti
si provvede a leggere o l'intero file (se il parametro
del numero delle righe da leggere è -1) creando
lo stream di lettura
BufferedReader
br = new BufferedReader(new FileReader(file));
if(numLines == -1) { // read all file required
while(br.ready()) {
String temp = br.readLine();
oRec.add(temp);
}
}
o
un numero specifico di righe richiesto
else
{
for(int i=0; i<numLines && (br.ready());
++i) {
String temp = br.readLine();
oRec.add(temp);
}
}
alla
fine dell'operazione di lettura viene chiuso lo stream
br.close();
Nel
caso invece si richieda un'operazione di scrittura
else if(procName.equals("WRITE")){
viene
verificata la compatibilità della classe del
parametro input con il MokaConnector
IndexedRecord
iRec = null;
if(!(input instanceof IndexedRecord) ) {
throw new ResourceException(
);
}
iRec = (IndexedRecord) input;
viene
poi creato lo stream di scrittura
PrintWriter pw = new PrintWriter(new BufferedWriter(
new FileWriter(file.getAbsolutePath(),file.exists())));
ogni
elemento contenuto nell'oggetto input record sarà
scritto sul file
for(int
i=0; i<iRec.size(); ++i) {
pw.println(iRec.get(i));
pw.flush();
}
alla
fine dell'operazione di lettura viene chiuso lo stream
pw.close();
e
viene restituito il Record di output
return
oRec;
// conn.close();
} catch(Exception ex) {
throw new ResourceException(ex.getMessage());
}
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 MokaConnector
Andiamo
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.JNDI_NAME_EIS);
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);
String
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
iSpec.setFunctionName("WRITE");
RecordFactory
rf = this.connectionFactory.getRecordFactory();
IndexedRecord
iRec = rf.createIndexedRecord("InputRecord");
si
valorizza l'indexed record con i dati del ListModel
ricevuto come parametro d'ingresso
for(int
i=0; i<vals.size(); ++i) {
iRec.add(vals.elementAt(i));
}
e
infine si invoca il metodo execute
ix.execute(iSpec,
iRec);
}catch(ResourceException ex) {
}
}
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();
Conclusioni
In 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
|