MokaByte 70- Gennaio 2003 
Java Connector Architecture
V parte: il Resource Adapter MokaConnector
di

S. Rossini
G. Morello

Nei precedenti articoli, abbiamo introdotto JCA nel suo complesso e abbiamo analizzato in dettaglio le API CCI e i System Contracts. In questo articolo completeremo lo studio di JCA esaminando l'implementazione del MokaConnector.

Introduzione
Per semplicità didattica, il MokaConnector gestisce come risorsa EIS un file testuale, senza addentrarsi nelle problematiche complesse legate a risorse EIS più evolute (HOST, ERP, …).
Come si è visto in [JCA2] e [JCA3], le API CCI sono il contratto tra il client J2EE ed il Resource Adapter mentre i System Contracts sono i contratti che si stipulano tra il Resource Adapter ed i servizi dell'Application Server J2EE ospitante [JCA4].
In questo articolo descriveremo le classi che compongono il Resource Adapter (RA) MokaConnector.

 

Le classi del Resource Adapter MokaConnector

Dalla tabella si vede come le classi si riferiscano al Connection Management ad eccezione della classe MokaConnectorSecurity che partecipa al Security Management. Il Transaction Contract non è gestito per l'intrinseca non-transazionalità della risorsa file.


Figura 1
- Le classi del ResourceAdapter MokaConnector

Ogni RA deve obbligatoriamente fornire l'implementazione delle tre interfacce SPI: ManagedConnectionFactory,ManagedConnection e ManagedConnectionMetaData.

 

La classe MokaManagedConnectionFactory
E' la classe del MokaConnector che implementa l'interfaccia javax.resource.spi.ManagedConnectionFactory

public class MokaManagedConnectionFactory implements
javax.resource.spi.ManagedConnectionFactory, java.io.Serializable {

Agisce da entry-point per la comunicazione tra l'AS ed il RA permettendo la creazione di connessioni con l'EIS.
Questa classe contiene i due importantissimi metodi createConnectionFactory() e createManagedConnection().
L'AS invoca il metodo createConnectionFactory() per richiedere un'istanza della classe ConnectionFactory del RA. Nel caso del MokaConnector il metodo ManagedConnectionFactory.createConnectionFactory() restituisce all'AS un oggetto di classe MokaCciConnectionFactory permettendo l'opportuno bind presso il JNDI Server.

public Object createConnectionFactory(ConnectionManager cm) throws ResourceException{
// return new MyContextFactoryImpl(….);
// Nel caso di RA NON CCI compliant !
this.file = new File(this.fileName);
return new MokaCciConnectionFactory(this, cm);
}

Quando l'AS neccessita della risorsa fisica EIS invoca il metodo ManagedConnectionFactory. CreateManagedConnection().

public ManagedConnection createManagedConnection(Subject subject,
                         ConnectionRequestInfo info)
                         throws ResourceException {

this.file = new File(this.fileName);
return new MokaManagedConnection(subject, this.file);
}

Nella realizzazione di un RA per DBMS, sarebbe stato ad esempio necessario creare una connessione JDBC (DataSource.getConnection()), passandola all'oggetto ManagedConnection.

La classe MokaManagedConnectionFactory contiene quindi al suo interno il factory della risorsa EIS che viene passata sia al MokaCciConnectionFactory che alla MokaManagedConnection.

Nel caso di un RA per DBMS la proprietà incapsulata sarebbe un oggetto di classe javax.sql.DataSource.
La classe MokaMagedConnectionFactory ha anche la responsabilità di gestire le proprietà del connettore dichiarate nel deployment descriptor XML.
Ogni proprietà definita nel file XML (mediante i tag <config-property-name> ,<config-property-type>,<config-property-value>) deve essere mappata in notazione JavaBean nella classe MokaCciManagedConnectionFactory.
Nel caso del MokaConnector nel file ra.xml si dichiara una sola proprietà relativa al nome del file EIS:


<config-property>
<config-property-name>FileName</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>/moka_jca/eis/prova.txt</config-property-value>
</config-property>

che viene mappata nella classe MokaManagedConnectionFactory nel seguente modo:

public class MokaManagedConnectionFactory
       implements ManagedConnectionFactory, Serializable {
private String fileName;
. . .
public void setFileName(String filename){
this.fileName = filename;
}
public String getFileName(){
return this.fileName;
}



Figura 2 -
Sequence diagram delle interazioni tra AS e RA durante la fase di deploy
(clicca sull'immagine per ingrandire)


ManagedConnection
E' la classe del MokaConnector che implementa l'interfaccia javax.resource.spi.ManagedConnection

public class MokaManagedConnection implements javax.resource.spi.ManagedConnection {

Incapsula la risorse EIS e fornisce i metodi necessari per stabilire la connessione fisica con l'EIS. Nel caso del MokaConnector questo si traduce nell'avere una proprietà di classe java.io.File.

private java.io.File file = null;

public java.io.File getFile() throws ResourceException {
if(this.file == null){
throw new ResourceException(" ### ERROR ! … ###");
}
else {
return this.file;
}
}

Nel caso di un RA per DBMS, la proprietà incapsulata sarebbe un oggetto java.sql.Connection.

La classe MokaManagedConnection mette a dispozione il metodo getConncetion() che permette di ottenere un riferimento alla connessione fisica della risorsa EIS

public java.lang.Object getConnection(javax.security.auth.Subject subject,
ConnectionRequestInfo cxRequestInfo) throws ResourceException

Tale metodo riceve in ingresso il contesto di sicurezza (javax.security.auth.Subject) e le informazioni riguardanti la richiesta della connessione (javax.resource.spi.ConnectionRequestInfo) e restituisce un'istanza MokaCciConnection.

public Object getConnection(javax.security.auth.Subject subject,
javax.resource.spi.ConnectionRequestInfo info)throws ResourceException {

if(MokaConnectorSecurity.isCredentialValid(subject, info)){
if(this.conn == null ){
conn = new MokaCciConnection(this);
}
}
. . .
return conn;
}

Dalla figura 3 si vede la relazione che intercorre tra le classi ConnectionFactory e ConnectionCCI e le corrispondenti SPI.


Figura 3 - Class diagram MokaConnector: relazione tra le classi
ConnectionFactory e Connection
(clicca sull'immagine per ingrandire)

Sempre mediante l'implementazione dell'interfaccia ManagedConnection è possible accedere alle interfacce javax.resource.spi.LocalTransaction e javax.transaction.xa.XAResource per la gestione delle transazioni del RA.
Inoltre il MokaManagedConnection permette inoltre di ottenere le informazioni descrittive d'interfaccia javax.resource.spi.ManagedConnectionMetaData mediante il metodo getMetaData()

public ManagedConnectionMetaData getMetaData() throws ResourceException{
return new MokaManagedConnectionMetaData(this);
}

 

ManagedConnectionMetaData
Invocando il metodo MokaManagedConnection.getMetaData() è possibile ottenere le informazioni descrittive incapuslate in un oggetto d'interfaccia ManagedConnectionMetaData

public class MokaManagedConnectionMetaData implements javax.resource.spi.ManagedConnectionMetaData {

Tale classe agisce di fatto da Wrapper per gli specifici Meta Data della risorsa EIS. Nel caso del MokaConnector l'implementazione è poco significativa visto che la risorsa EIS è un semplice file.
Nel caso di un Resource Adapter per DBMS verrebbero forniti i dati ricavati mediante l'interfaccia java.sql Interface DatabaseMetaData

public class MetaDataImpl implements ManagedConnectionMetaData {

private java.sql.Connection con;

public MetaDataImpl(Connection c) { this. con= c; }

public String getEISProductName() throws ResourceException {
  try{ return con.getMetaData().getDatabaseProductName(); }
  catch(….){ throw …}
}
public int getMaxConnections() throws ResourceException {
  try { return con.getMetaData().getMaxConnections(); }
  catch(….){ throw …}
}
<< etc … >>
}

 

ConnectionRequestInfo
Fornisce le informazioni che descrivono i dati necessari alla richiesta di connessione all'EIS.
Nel caso del MokaConnector viene specificata (per puro scopo dimostrativo) la userid di accesso al file.
Nel caso non si dovessero passare dati specifici per la connessione richiesta, è possibile utilizzare null come secondo parametro del metodo allocateConnection().
Nel caso invece si voglia implementare tale interfaccia, la classe concreta deve obbligatoriamente fornire i metodi equals e hashcode per "aiutare" l'AS nella gestione del pooling di connessioni.

public class MokaConnectionRequestInfo
             implements ConnectionRequestInfo{

private String eisAccount; // utenza per accedere al file

public MokaConnectionRequestInfo(String usr){ this.eisAccount=usr;}
public String getEisAccount() {return this.eisAccount;}
public boolean equals(Object obj) { . . . }
public int hashCode() {. . . }
private boolean isEqual(Object o1, Object o2) { . . . }
}


Il Transaction Management del MokaConnector
Mediante l'implementazione dell'interfaccia ManagedConnection è possible accedere alle interfacce javax.resource.spi.LocalTransaction per la gestione delle transazioni locali al RA e javax.transaction.xa.XAResource per la gestione delle transazioni distribuite.
Nel semplice caso del MokaConnector l'implementazione dei metodi si limita a lanciare una ResourceException, essendo il file una risorsa di natura non transazionale

public LocalTransaction getLocalTransaction() throws ResourceException{
throw new ResourceException("Local transaction are NOT SUPPORTED by MokaConnector #");
}

public XAResource getXAResource() throws ResourceException {
throw new ResourceException("XA transaction are NOT SUPPORTED by MokaConnector #");
}

Il Security Management nel MokaConnector
Il metodo ManageConnection.getConnection() riceve come parametri di input un oggetto Subject con i dati di sicurezza dell'AS ed un oggetto d'interfaccia ConnectionRequestInfo.

public Object getConnection(Subject subject,
                            ConnectionRequestInfo info)
                            throws ResourceException {

Alla classe MokaConnectorSecurity viene chiesta l'autenticazione del Subject e la validazione dei dati ConnectionRequestInfo.
La classe MokaConnectorSecurity offre dei controlli di sicurezza elementari, ma rappresenta un ottimo punto per concentrare le eventuali estensioni del sistema di sicurezza del RA (es. JAAS).
L'oggetto Subject è valorizzato dall'AS con modalità proprietarie. Nel caso di JBoss, i valori vengono inseriti nei tag Principal all'interno della dichiarazione del bean JMX relativo al RA:

<!-- The Moka example filesystem resource adapter -->
<mbean code="org.jboss.resource.ConnectionFactoryLoader"
name="JCA:service=ConnectionFactoryLoader,
name=NoTransMokaConnectorFS">

. . . . .
<!-- Principal mapping configuration -->
<attribute name="PrincipalMappingClass">
  org.jboss.resource.security.ManyToOnePrincipalMapping
</attribute>
<attribute name="PrincipalMappingProperties">
userName=mokauser
password=abc123
</attribute>
</mbean>

Il Tester EJB client crea l'EJB di test specificando nel metodo create la userid inserita dall'utente da tastiera

Tester remoteReference = home.create(userid);

Nel metodo ejbCreate del TesterBean viene costruito un oggetto di classe MokaCciConnectionSpec

MokaCciConnectionSpec spec = new MokaCciConnectionSpec(userid);

L'oggetto MokaCciConnectionSpec viene passato come parametro al metodo ConnectionFactory.getConnection()

Connection con = cf.getConnection(spec);

All'interno del metodo ConnectionFactory.getConnection() viene creato un oggetto ConnectionRequestInfo con i dati contenuti nell'oggetto MokaCciConnectionSpec

public javax.resource.cci.Connection getConnection(ConnectionSpec properties) throws ResourceException {
ConnectionRequestInfo info =
new MokaConnectionRequestInfo(
((MokaCciConnectionSpec)properties).getUser(),
((MokaCciConnectionSpec)properties).getPassword());

per poi essere passato come parametro del metodo ConnectionManager.allocateConnection()

con = (Connection)this.connectionManager.allocateConnection(
this.managedConnectionFactory,info);

L'AS a questo punto invoca il metodo MokaManaged.getConnection() che al suo interno demanda la gestione della sicurezza alla classe MokaConnectorSecurity.
Se i check di sicurezza (MokaConnectorSecurity.isCredentialValid()) hanno esito positivo, viene creata (finalmente!) una connessione alla risorsa EIS

public Object getConnection(Subject subject,
                            ConnectionRequestInfo info)
                            throws ResourceException {

if(MokaConnectorSecurity.isCredentialValid(subject, info)){
if(this.conn == null ){
conn = new MokaCciConnection(this);
}
}
else{
throw new ResourceException("# SecurityException #
Subject or Credentials NON VALIDE!");
}
return conn;
}

Il MokaConnector in azione: deploy ed esecuzione
Conclusa la panoramica del codice delle classi Resource Adapter del MokaConnector, vediamo ora come effettuare deploy ed esecuzione in un ambiente JBoss [JBOSS].

 

Il file ra.xml
Ogni RA è "accompagnato" da un file deployment descriptor di nome ra.xml posizionato sotto la directory META­INF (META­INF/ra.xml).
Il deployment descriptor contiene le seguenti informazioni:

  • informazioni generiche e descrittive del RA

    <display-name>File System Resource Adapter</display-name>
    <vendor-name>MokaByte S.R.L</vendor-name>
    <spec-version>1.0</spec-version>
    <version>1.0</version>
    <eis-type>FileSystem</eis-type>
    <license>
    <description>Mokabyte simple example</description>
    <license-required>false</license-required>
    </license>

  • interfacce e relative classi concrete per accedere al connettore

    <managedconnectionfactory-class>
    it.mokabyte.mokajca.ra.MokaManagedConnectionFactory
    </managedconnectionfactory-class>
    <connectionfactory-interface>
    javax.resource.cci.ConnectionFactory</connectionfactory-interface>
    <connectionfactory-impl-class>
    it.mokabyte.mokajca.ra.cci.MokaCciConnectionFactory
    </connectionfactory-impl-class>
    <connection-interface>
    javax.resource.cci.Connection
    </connection-interface>
    <connection-impl-class>
    it.mokabyte.mokajca.ra.cci.MokaCciConnection
    </connection-impl-class>


  • supporto Transazionale
    <transaction-support>NoTransaction</transaction-support>

  • informazioni di autenticazione (<authentication-mechanism>)

  • permessi di sicurezza (<security-permission>)

  • proprietà di configurazione

    <config-property>
    <config-property-name>FileName</config-property-name>
    <config-property-type>java.lang.String</config-property-type>
    <config-property-value>
      /moka_jca/eis/prova.txt
    </config-property-value>
    </config-property>

Contiene quindi le informazioni necessarie per effettuare sia il deploy che la configurazione del RA per un AS JCA-enabled.

 

Deploy del Resource Adapter
Per effettuare il deploy del RA è necessario archiviare tutti i file in un jar di estensione .rar.
Il RA risulta quindi costituito da classi Java che implementano sia le API CCI, le classi del RA, gli eventuali driver nativi e l' XML deployment descriptor. Questi file sono tutti archiviati nel medesimo file jar che prende il nome di Resource ARchive file (RAR); un file RAR è l'unità di distribuzione del RA.
Una volta creato il file RAR è possibile effettuare l'operazione di deploy del RA in un server J2EE, analogamente a quanto accade per gli EJB jar, i file war o gli stessi file ear.
La procedura di deploy è vendor-dependent, cioè varia a secondo dell'AS che si utilizza.
Ad esempio con JBoss è sufficiente posizionare il file RAR nella directory <JBossHome>\deploy\lib. Il deploy è automatico e a carico dell'AS.

 

Deploy dell'EJB di test del MokaConnector
L'EJB di test utilizza il MokaConnector esplicitando come resource-reference il nome logico ra/CCI_MOKA_EIS nel file ejb-jar.xml

<ejb-jar>
<enterprise-beans>
<session>
<ejb-name>MokaEjbTestRA</ejb-name>
<home>it.mokabyte.mokajca.ejb.TesterHome</home>
<remote>it.mokabyte.mokajca.ejb.Tester</remote>
<ejb-class>it.mokabyte.mokajca.ejb.TesterBean</ejb-class>
<session-type>Stateful</session-type>
<transaction-type>Container</transaction-type>
<resource-ref>
<res-ref-name>ra/CCI_MOKA_EIS</res-ref-name>
<res-type>it.mokabyte.mokajca.ra.cci.MokaCciRecordFactory</res-type>
<res-auth>Container</res-auth>
</resource-ref>
</session>
</enterprise-beans>
</ejb-jar>

All'interno del file vendor-dependent (jboss.xml) si ha il mapping tra il riferimento del connettore ed il nome logico JNDI a cui corrisponde la risorsa fisica del MokaConnector

<jboss>
<enterprise-beans>
<session>
<ejb-name>MokaEjbTestRA</ejb-name>
<resource-ref>
<res-ref-name>ra/CCI_MOKA_EIS</res-ref-name>
<jndi-name>java:NoTransMokaConnectorFS </jndi-name>
</resource-ref>
<jndi-name>MokaConnectorEjbTester</jndi-name>
</session>
</enterprise-beans>
</jboss>

La configurazione dell'Mbean del ConnectionManager è definita nel file <JBOSS_HOME>\conf\default\jboss.jcml>

<mbean code="org.jboss.resource.ConnectionFactoryLoader"
name="JCA:service=ConnectionFactoryLoader,name=NoTransMokaConnectorFS">
<attribute name="FactoryName">NoTransMokaConnectorFS</attribute>
<attribute name="RARDeployerName">JCA:service=RARDeployer</attribute>
<attribute name="ResourceAdapterName">File System Adapter</attribute>
<attribute name="ConnectionManagerFactoryName">MinervaNoTransCMFactory</attribute>
<!-- See the documentation for the specific connection manager
implementation you are using for the properties you can set -->
<attribute name="ConnectionManagerProperties" />

<!-- Principal mapping configuration -->
<attribute name="PrincipalMappingClass">
  
org.jboss.resource.security.ManyToOnePrincipalMapping
</attribute>
<attribute name="PrincipalMappingProperties">
userName=mokauser
password=abc123
</attribute>
</mbean>

 

Esecuzione dell'esempio
L'esecuzione del Tester EJB Client produrrà la lettura/scrittura del file "/moka_jca/eis/prova.txt".


Figura 4 -
Esecuzione del test del MokaConnector

 

Conclusioni
In questo articolo abbiamo esaminato l'implementazione del MokaConnector, concludendo la serie dedicata a JCA. L'esempio pratico presentato riassume e concretizza quanto presentato su JCA nei precedenti numeri di MokaByte.
Allegati

 

Risorse
L'esempio completo del MokaConnector è scaricabile qui

 

Bibliografia e riferimenti
[JCA1] S.Rossini-G.Morello: "JCA: I parte: la teoria", Mokabyte N.66 - Settembre 2002
[JCA2] S.Rossini-G.Morello: "JCA: II parte: CCI la teoria", Mokabyte N.67 - Ottobre 2002
[JCA3] S.Rossini-G.Morello: "JCA: III parte: CCI la pratica", Mokabyte N.68 - Novembre 2002
[JCA4] S.Rossini-G.Morello:"JCA: IV parte: il Resource Adapter e i System Contracts", Mokabyte N.69 - Dicembre 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
[JB_DOC] Scott Stark, Mark Fleury : JBoss Administration and Development

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