Progettazione, sviluppo e gestione del change con modelli UML, framework e strumenti open source

III parte: pubblicazione della logica applicativadi

Passiamo a definire la logica applicativa, completando il workspace con la sua codifica e la sua distribuzione sui middleware. Ci occupiamo inoltre di introdurre due progetti di utilità, uno di automazione della distribuzione e l'altro di verifica.

Introduzione

Dopo aver presentato i progetti che definiscono la logica applicativa, in questa parte completeremo il workspace con la codifica della tecnologia di pubblicazione e la distribuzione di quest'ultima sui middleware di esecuzione. Vedremo come le scelte di implementazione e di packaging del software realizzeranno la modellazione UML e il modello di sviluppo presentati nella prima parte di questa serie di articoli. L'introduzione nel workspace di due progetti di utilità, uno di automazione della distribuzione e l'altro di verifica di quest'ultima, contribuiranno a rendere attuabile la conduzione della fase realizzativa in attività indipendenti e parallele, eseguibili da gruppi di lavoro diversi.

Progetti di pubblicazione della logica applicativa e modello di sviluppo

Nella modellazione UML presentata nella prima parte, troviamo la definizione della tecnologia di esposizione della logica applicativa nel diagramma di distribuzione (cfr. I parte, figura 10), dove la libreria corrispondente alla logica di esposizione del servizio viene collocata nel nodo che rappresenta il contenitore degli Enterprise JavaBeans dell'Application Server. Il pattern di sviluppo, modellato nel diagramma di struttura composita (cfr. I parte, figura 8) e la cui descrizione è iniziata nel precedente articolo dove abbiamo separato l'implementazione dall'interfaccia, viene qui completato con una codifica che separa la tecnologica di esposizione del servizio dalla sua interfaccia. Risolviamo questo orientamento attraverso un' implementazione di Spring, semplificando la codifica di un EJB Session Stateless e applicando nello stesso tempo il modello di sviluppo scelto. Quest'ultimo, attraverso l'isolamento della classe di implementazione dalla tecnologia di pubblicazione, può rappresentare una opportunità nel caso in cui ci sia la necessità di un porting della logica applicativa su una tecnologia di pubblicazione diversa [1].

Progetto con la logica di pubblicazione

In questo progetto viene codificata la logica di esposizione dei metodi del componente sotto forma di un EJB Session Stateless, secondo quanto descritto dalla versione del diagramma dei componenti che specializza le tecnologie (I parte, figura 7). Riportiamo di seguito il progetto evidenziando la classe che implementa l'EJB (in blu) e le dipendenze verso il run-time di WebSphere e il framework di Spring (ambedue in rosso).

 

 

Figura 1 - Progetto EJB: evidenza delle dipendenze.

 

Analizziamo nel dettaglio le modalità con cui realizziamo il pattern di sviluppo. Il diagramma delle classi che riportiamo di seguito esprime, per la classe che implementa la tecnologia di pubblicazione, la dipendenza verso l'interfaccia del servizio, e la relazione di estensione della superclasse AbstractStatelessSession del framework di Spring. Quest'ultima semplifica la codifica di uno Stateless Session Bean eliminando tutta la complessità di implementazione dell'interfaccia javax.ejb.SessionBean.

 

 

Figura 2 - Diagramma delle classi: interfaccia del servizio, implementazione dell'EJB e di Spring.

 

Riportiamo di seguito il dettaglio della codifica della classe Servizio01EJBBean, l'unica da implementare in questo progetto, una volta definita l'interfaccia del servizio. A partire da questa, le restanti classi (Home, Locale e Remota) verranno generate dall'ambiente di sviluppo.

package it.luigibennardis.servizio01.ejbs;
 
import java.util.Date;
import javax.ejb.CreateException;
 
import it.luigibennardis.ejbs.servizio01.iface.IServizio01;
import it.luigibennardis.servizio01.dto.Cliente;
import it.luigibennardis.servizio01.dto.Ordine;
 
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.support.FileSystemXmlApplicationContext;
import org.springframework.ejb.support.AbstractStatelessSessionBean;
 
public class Servizio01EJBBean extends AbstractStatelessSessionBean implements IServizio01 {
 
       static final long serialVersionUID = 3206093459760846163L;
            
       private static final String APPLICATION_CONTEXT 
          = "C:/workspaces/……/servizio01_ctx.xml";
        
       private ListableBeanFactory beanFactory;
      
//***NOME DEL BEAN DEFINITO NELL'APPLICATION CONTEXT
       private static final String BEAN_NAME = "Servizio01";  
      
//***INTERFACCIA DEL SERVIZIO    
private IServizio01 service;  
      
      
       public void ejbCreate() throws CreateException {
             this.beanFactory = new FileSystemXmlApplicationContext(APPLICATION_CONTEXT);
             onEjbCreate();
       }
      
       protected void onEjbCreate() throws CreateException {
              
              service = (IServizio01) getBeanFactory().getBean(BEAN_NAME);
       }
         
       protected BeanFactory getBeanFactory() {
                    return this.beanFactory;
}
 
      
//*** METODI WRAPPER DELLA CLASSE CHE IMPLEMENTA LA LOGICA APPLICATIVA
       public Cliente dettaglioCliente(String codCliente) {
                    return service.dettaglioCliente(codCliente);
             }
      
       public Ordine dettaglioOrdineClienteData(String codCliente, Date data) {
                    return service.dettaglioOrdineClienteData(codCliente, data);
             }
      
       public Ordine[] listaOrdiniCliente(String codCliente) {
                    return service.listaOrdiniCliente(codCliente);
             }
      
}

Come dichiarato nel diagramma delle classi, nel codice non c'è  nessun riferimento a classi Java che implementano logica applicativa. Attraverso Spring codifichiamo infatti l'EJB sotto forma di wrapper tecnologico dell' interfaccia, e sempre con l'integrazione di questo framework iniettiamo la classe di implementazione desiderata.

Dall'esame del listato notiamo infatti le import delle classi che rappresentano il contesto di implementazione della injection di Spring:

import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.context.support.FileSystemXmlApplicationContext;

Vediamo poi la import delle classi che rappresentano il contesto di implementazione di Spring-EJB:

import org.springframework.ejb.support.AbstractStatelessSessionBean;

nonche' la costante statica che definisce il percorso di pubblicazione del file di contesto:

private static final String APPLICATION_CONTEXT = "C:/…/servizio01_ctx.xml";

Quest'ultima scelta è stata fatta per agevolare l'esecuzione dei progetti pubblicati in allegato: in un contesto reale sarà necessario definire, a livello di configurazione dell'application server, la risoluzione di questo file attraverso una Resource Property.

Abbiamo poi la codifica del metodo ejbCreate() della superclasse org.springframework.ejb.support.AbstractStatelessSessionBean dove viene inizializzato il contesto di Spring:

public void ejbCreate() throws CreateException {
             this.beanFactory = new FileSystemXmlApplicationContext(APPLICATION_CONTEXT);
             onEjbCreate();
       }

Oltre a questo c'è la codifica del metodo onEjbCreate() dove viene iniettata la classe di implementazione:

protected void onEjbCreate() throws CreateException {
                            
       service = (IServizio01) getBeanFactory().getBean(BEAN_NAME);
}

e la definizione dei metodi wrapper dell'interfaccia:

       public Cliente dettaglioCliente(String codCliente) {
                    return service.dettaglioCliente(codCliente);
             }
      
       public Ordine dettaglioOrdineClienteData(String codCliente, Date data) {
                    return service.dettaglioOrdineClienteData(codCliente, data);
             }
      
       public Ordine[] listaOrdiniCliente(String codCliente) {
                    return service.listaOrdiniCliente(codCliente);
             }

Riportiamo di seguito il contenuto del file di contesto di Spring dove viene dichiarata la classe di implementazione da iniettare.


                                                         /spring-beans.dtd">

                

Progetto di definizione del pacchetto di distribuzione

Con questo progetto generiamo il pacchetto di distribuzione del componente realizzato. Una applicazione Java EE viene distribuita attraverso un file Enterprise ARchive (EAR), un Java ARchive (JAR) con estensione .ear, dove vengono inclusi i moduli o applicazioni Java EE desiderati, i descrittori di distribuzione (file XML di configurazione di ciascun modulo) ed eventuali librerie di dipendenza.

Nel nostro caso, l'articolazione dell'EAR, rappresentata dal diagramma di distribuzione (parte I, figura 10), è costituita dalla libreria che implementa la tecnologia di pubblicazione del servizio (EJB_Servizio01.jar) quale modulo Java EE da distribuire nel container degli EJB.

Diversamente da quanto modellato, per agevolare la distribuzione e non costringere il lettore in dettagli di configurazione dell'Application Server, abbiamo incluso nell'EAR, come librerie di utilità, anche le librerie di interfaccia e il client EJB. Queste avrebbero dovuto avere una diversa distribuzione, in uno dei classpath dell'Application Server, dove sono pubblicate le librerie condivise da tutte le applicazioni Java EE. Diversamente, in ogni EAR di ciascuna delle applicazione Java EE distribuite nello stesso Application Server dovrebbero essere presenti tutte le librerie EJB client e le interfacce di tutte le applicazioni perche' siano accedibili localmente o sul protocollo remoto.

In sintesi gli scenari di distribuzione sono due:

  • una modifica dell'interfaccia del servizio (SERVIZIO01_IFACE.jar) determina una nuova implementazione del wrapper tecnologico (EJB_Servizio01.jar) e una nuova generazione del proxy (EJB_Servizio01Client.jar);
  • a interfaccia costante, e seguendo il deployment model, è possibile ridistribuire nuove librerie di implementazione il cui bind viene risolto nel wrapper EJB adeguando il file di contesto di Spring e riavviando l'applicazione Java EE.

 

 

Figura 3 - Progetto di distribuzione.

 

Progetto di automazione della distribuzione

Attraverso questo progetto Ant, automatizziamo la distribuzione di ogni  nuova implementazione della logica applicativa, agevolando quei gruppi che pur avendo una esigenza di questo tipo non saranno obbligati ad avere conoscenze di distribuzione Java EE. Attraverso l'automazione definita in questo progetto, i sorgenti verranno compilati e sottoposti ai test JUnit previsti, ne verrà creata la libreria e il pacchetto di distribuzione corrispondente che verrà quindi distribuito sul nodo previsto.

Nel progetto riportato di seguito, evidenziamo in blu i descrittori di distribuzione specifici per il container scelto.

 

 

Figura 4 - Progetto di automazione della distribuzione.

 

Infine riportiamo la codifica del file Ant che automatizza il processo. Per il task di undeploy e deploy dell'EAR rimandiamo il lettore al paragrafo "Task di distribuzione su un server remoto" dell'articolo "Cruise Control open source framework" [2].



       
                    1) COMPILA I SORGENTI DELLA CLASSE DI IMPLEMENTAZIONE
                    2) CREA LA LIBRERIA CORRISPONDENTE
                    3) APPLICA I JUNIT AUTOMATICI (SE 3 OK ESEGUE 4 E 5
                    4) CREA EAR DI PUBBLICAZIONE
                    5) (UNDEPLOY EVENTUALE)DEPLOY DELL'EAR SUL MIDDLEWARE
       
            
       
       
                           ="../SERVIZIO01_EXPORT_EAR/EXPORT/SERVIZIO01_TST.jar" />
                           ="../SERVIZIO01_EXPORT_EAR/EXPORT/SERVIZIO01_IMPL.jar" /> 
                                    ="../SERVIZIO01_EXPORT_EAR/EXPORT/SERVIZIO01_IFACE.jar" />
                  ="libAppDTO" location="../APPLICAZIONE_DTO_BUILD/EXPORT/APPLICAZIONE_DTO.jar" />
       value="../LIBRERIE_COMUNI_RUNTIME/junit-4.8.1/junit-4.8.1.jar" /> 
       
            

             

      
description="Impacchetta l'EAR solo se i test definiti sono andati bene " depends="test">
       
            
       
                appxml="../SERVIZIO01_EXPORT_EAR/EAR/META-INF/application.xml">
             
                          
             
             
                    
             
                          
             
                    
             
                   
             
             
                    
             
                          
             
             
                    
             
                   
                 
             
             
                     
             
             
                       
             
                   
             
             
             
                    
             
                          
             
       
      
            
       
             
                           destdir="../SERVIZIO01_IMPL/build"
                    debug="true"
                    classpath="../SERVIZIO01_EXPORT_EAR/EXPORT/SERVIZIO01_IFACE.jar:
                       ../APPLICAZIONE_DTO_BUILD/EXPORT/APPLICAZIONE_DTO.jar" />
       
              
                    description="Esporta il jar con le classi di implementazione compilate">
             
                          basedir="../SERVIZIO01_IMPL/build" />
      
       
            
       
             
              
             
              
            
             
              
              
              
            
            
       
            
             
                                 includes="**/Servizio*Test*.class" />
            
             
                    ESECUZIONE DI TUTTE LE CLASSI JUNIT **/Servizio*Test*.class
                    SULLA LIBRERIA ${testDir}
             
             
             
                    
                    
                    
                           
                           
                    
             
            
             message="TEST JUNIT FALLITI:CONFRONTARE LA REPORTISTICA ${reportDir}"
if="test.failure" />
              
                 

Progetto di verifica della distribuzione

L'ultimo presentato è un progetto di utilità finalizzato alla verifica che il servizio distribuito sia correttamente raggiungibile e consumabile. Si tratta di un progetto JavaStandard, implementato anch'esso con il supporto di Spring ed eseguibile al di fuori del contesto applicativo. L'integrazione con il framework di Spring semplifica e rende flessibile la  risoluzione dell'EJB e la sua invocazione sul protocollo remoto. Nel file di contesto riportato di seguito, sono infatti facilmente distinguibili i marcatori corrispondenti alla risoluzione del servizio e quelli relativi alle classi di comodo fornite da Spring per l'invocazione sul protocollo remoto di un EJB Session Stateless.


                                                  /dtd/spring-beans.dtd">

       
                    class
             ="org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean"
             singleton="false">
        
         
                  value="nodes//servers/server1/ejb/it
                      /luigibennardis/servizio01/ejbs/Servizio01EJBHome"/>
        
         
         
              
                             ="java.naming.factory.initial">com.sun.jndi.cosnaming.CNCtxFactory
             iiop://localhost:2810
       
              
             
              
              
                     it.luigibennardis.servizio01.iface.IServizio01
              

Ne risulta una codifica di invocazione molto semplificata:

public class TestEJB {
       public static final String CLIENT_CONTEXT_CONFIG_LOCATION 
                       = "file:**/ejbClientContext.xml";
       public static void main(String[] args) {
ListableBeanFactory beanFactory 
    = new FileSystemXmlApplicationContext(CLIENT_CONTEXT_CONFIG_LOCATION);
       IServizio01 bean = (IServizio01) beanFactory.getBean("IServizio01");
       Cliente localCliente = new Cliente();
       localCliente = bean.dettaglioCliente("AAA01");
}

Conclusioni

In questa terza parte, abbiamo concluso la presentazione del workspace di sviluppo con i progetti che implementano la tecnologia di esposizione della logica applicativa. Attraverso l'introduzione di Spring abbiamo inoltre completato il pattern di sviluppo iniziato nella parte precedente e semplificato la complessità della codifica della stessa tecnologia di pubblicazione.

Nelle prossima parte definiremo e configureremo lo strumento che sarà utilizzato per la gestione della configurazione, e integreremo ulteriormente il workspace di sviluppo con i progetti strettamente connessi all'operatività del gruppo che si occuperà della gestione della configurazione del software.

Riferimenti

[1] Luigi Bennardis, "Realizziamo un Web Service con Spring-WS - I parte: Approccio allo sviluppo e primi passi", MokaByte 147, gennaio 2010

http://www2.mokabyte.it/cms/article.run?articleId=M27-BXP-PDN-Q9Y_7f000001_18359738_27da7fdb

 

[2] Luigi Bennardis, "Cruise Control open source framework. Un tool estensibile per la Continuous Integration ", MokaByte 141, giugno 2009

http://www2.mokabyte.it/cms/article.run?articleId=IIH-SI7-IF6-76H_7f000001_10911033_04a6b378

 

[3] Java EE: EJB

http://www.oracle.com/technetwork/java/javaee/ejb/index.html

 

[4] Java EE: EAR

http://docs.oracle.com/javaee/1.4/tutorial/doc/Overview5.html

 

[5] Ant

http://ant.apache.org/

 

[6] Spring

http://www.springsource.org/

Condividi

Pubblicato nel numero
178 novembre 2012
Luigi Bennardis si è laureato in Scienze Statistiche ed Economiche all’università di Roma e si occupa di informatica da diversi anni. Dopo una lunga esperienza di system integration su piattaforma Java EE e Microsoft in una importante società di consulenza, attualmente si occupa di progettazione e sviluppo su tecnologie distribuite…
Ti potrebbe interessare anche