MokaByte 74- Maggio 2003 
Integrazione di applicazioni Enterprise
II parte
di
Stefano Rossini
Nello scorso articolo (vedi [INT1]) sono state presentate le tre semplici applicazioni enterprise che saranno oggetto di prove d'integrazione. In questo articolo si parlerą dell'integrazione a livello d'interfaccia utente e a livello di O-bect/RPC.

Introduzione
Nello scorso articolo (vedi [INT1]) sono state presentate le tre semplici applicazioni enterprise che saranno oggetto di prove d'integrazione.
In questo articolo si parlerà dell'integrazione a livello d'interfaccia utente e a livello di O-bect/RPC.
Integrazione interfaccia utente
Mediante l'integrazione a livello di User Interface si fa in modo che applicazioni indipendenti tra loro presentino la medesima interfaccia utente.
Questo tipo di approcio è consigliabile laddove non si necessiti distinguere da quale applica-zione provengano i dati e quindi risulti sufficiente una integrazione a "grana grossa" lato front-end.
Vediamo alcuni esempi pratici utilizzando le applicazioni A,B e C.
Una possibile modalità d'integrazione consiste nell'integrare le Applicazioni sviluppando un nuovo controller con il compito di invocare le risorse appartenenti all'Applicazione A ,B o C, a seconda della richiesta dell'utente.
A tale scopo si sviluppa una nuova pagina HTML che prevede il passaggio di un parametro di nome "DESTINAZIONE" il cui valore discrimina l'applicazione che si vuole utilizzare:

<HTML>
. . . .
<FORM NAME="F1" METHOD=GET ACTION="<%=request.getContextPath()%>/ServiceDispatcher.jsp" >
<input type="submit" name="DESTINAZIONE" value="APPLICATION_A">
</FORM>
. . . .
<FORM NAME="F1" METHOD=GET ACTION="<%=request.getContextPath()%>/ServiceDispatcher.jsp" >
<input type="submit" name="DESTINAZIONE" value="APPLICATION_B">
</form>
. . . .
<FORM NAME="F1" METHOD=GET ACTION="<%=request.getContextPath()%>/ServiceDispatcher.jsp" >
<input type="submit" name="DESTINAZIONE" value="APPLICATION_C">
</form>
. . . .

Il nuovo controller (nel nostro caso una semplice JSP di nome ServiceDispatcher.jsp) ha il compito di effettuare la corretta invocazione della risorsa richiesta.
Il ServiceDispatcher provvede a leggere il valore del parametro DESTINAZIONE

<% String action=request.getParameter("APPLICAZIONE");

e nel caso si riferisca all'applicazione A, verrà invocata la pagina concat.jsp mediante il meto-do forward del RequestDispatcher ottenuto dal ServletContext (l'oggetto implicito application che fa riferimento alla web application corrente).

if(action.equals("APPLICATION_A")){
RequestDispatcher rd = application.getRequestDispatcher("/jsp/concat.jsp");
rd.forward(request, response);
}

Nel caso dell'Applicazione B o C si utilizza il metodo Response.sendRedirect() che redireziona il client ad una nuova URL indicata nella Response (da notare come l'URL che compare nella navigation bar del browser sia quello reimpostato dal server).

else if(action.equals("APPLICATION_B")){
response.sendRedirect("http://manowar:7001/app_b/jsp/wordlength.jsp");
}
else if(action.equals("APPLICATION_C")){
response.sendRedirect(http://manowar:80/app_c/asp/wordlength.asp");
}
. . . . .
%>

Essendo le applicazioni A e B tecnologicamente omogenee (J2EE), si potrebbe installare la web application dell'applicazione B sul Servlet Engine dell'applicazione A (nel nostro caso Tomcat) ed avere la possibilità di ottimizzare l'invocazione delle risorse dell'applicazione B.
Infatti in questo caso il dispatcher potrebbe ottenere il riferimento al ServletContext della We-bApp B, richiedere il RequestDispatcher relativo alla risorsa d'interesse, ed effettuare l'opportuno forward senza dovere ricorrere al sendRedirect.

<%
. . .
else if(action.equals("APPLICATION_B")){
ServletContext sc = application.getContext("/app_b ");
RequestDispatcher rd = sc.getRequestDispatcher("/jsp/wordlength.jsp");
rd.forward(request,response);
}
. . . . .
%>



Figura 1 - Interoperabilità a livello d'interfaccia utente mediante l'utilizzo di un Dispatcher

Creato il dispatcher delle due applicazioni, si deve procedere ad uniformare il look & feel dell'applicazione integrata(app. B) a quella integrante(app. A).
Tali modifiche possono variare da semplici modifiche di rendering grafico (modifiche di size, font, layout della pagina) fino a "pesanti" e impegnative modifiche di fogli di stile, java script, action, …; è bene quindi valutare a priori il costo di questa operazione.
Nel nostro semplice esempio il tutto si risolve modificando il tipo e la dimensione dei font e cambiando la pagina template d'intestazione.
Nel caso in cui le due differenti WebApp da integrare si trovino sullo stesso Web Container è possibile "puntare" alle risorse dell'applicazione A all'interno delle pagine JSP dell'applicazione B come nel seguente esempio:

<%
ServletContext sc = application.getContext("/app_a");
RequestDispatcher rd = sc.getRequestDispatcher("/jsp/header.jsp");
rd.include(request,response);
%>

Questo approcio richiede l'impegno di effettuare il "porting" della presentation dell'applicazione B sull'application Server dell'applicazione A permettendo però di avere van-taggi come l'utilizzo di pagine template, il comportamento omogeneo su differenti browser e la possibilità di condividere la sessione HTTP.

Concludiamo con un'altra possibile modalità d'integrazione a livello d'interfaccia che prevede l'utilizzo dei frameset HTML. In questo modo è possibile presentare un documento con delle form indipendenti tra loro che "puntano" ai Server delle applicazioni d'interesse.
Il vantaggio di questo approcio è la facile implementazione e il "basso costo" di realizzazione limitandosi però ad una integrazione "superficiale" a livello di front-end. Possibili difficoltà a cui si andrebbe incontro sono la gestione del focus del frame, la sincronizzazione delle frames, la modalità di passaggio dati da un frame all'altro, l'apertura di una connnessione per ogni frame, le dimensioni dipendenti dalla scrollbar, le differente implementazione a secondo del browser,…
Nell'esempio sotto riportato si visualizzano in un'unica pagina tre diversi frame ciascuno dei quali visualizza la pagina iniziale delle Applicazioni A,B e C:

<html>
. . .
<frameset rows="*,*,*">
<frame name="primo" src="/eai/jsp/concat.jsp">
<frame name="secondo" src="http://localhost:7001/wls_webApp/jsp/wordlength.jsp">
<frame name="terzo" src="http://localhost/myiis/asp/wordlength.asp">
</frameset>
</html>


Figura 2
-Utilizzo dei FRAMESET HTML

 

Object/RPC integration
L'integrazione Object/RPC prevede di affrontare l'integrazione a livello di oggetti distributi (EJB, CORBA, DCOM, …), cioè mediante l'invocazione RPC dei metodi di business degli oggetti remoti.
Per integrare maggiormente l'applicazione A e l'applicazione B è possibile consentire l'invocazione diretta del servizio di calcolo della lunghezza della stringa da parte dell'applicazione A, senza dovere essere obbligati a navigare tra le pagine JSP dell'applicazione B.
Si vuole quindi che una volta inserite due stringhe da concatenare avvenga un'interazione diretta tra l'Applicazione A ed il servizio di calcolo lunghezzza stringa
In pratica si desidera che la pagina concatExecute.jsp invochi direttamente sia la concatenazio-ne delle stringhe l'EJB ConcatBean installato su JBoss) che il calcolo della lunghezza della stringa concatenata (EJB CalculatorBean installato su Weblogic Server 7).


Figura 3
: Integrazione Object/RPC da Business Delegate

Vediamo le modifiche che bisogna apportare alle classi dell'applicazione A al fine di permette-re l'interazione diretta con l'EJB Calculator dell'Applicazione B.
Innanzitutto bisogna "potenziare" la classe MyBusinessDelegate per potere essere in grado di invocare il metodo getLength() del servizio Calculator.
Si aggiunge quindi un nuovo attributo alla classe MyBusinessDelegate che altro non è che il reference remoto all'EJB Calculator.

public class MergeBusinessDelegate{

private Concat concat = null;
private Calculator calculator = null;

Nel costruttore si aggiuge la localizzazione dell'EJB Calculator

public MergeBusinessDelegate() throws BusinessDelegateException {
  try {
    CalculatorHome homeCalculator = (CalculatorHome)
                   MergeServiceLocator.getInstance().
                   getHome(Defines.CALCULATOR_SERVICE);
    this.calculator = homeCalculator.create();
    ...


e si aggiunge un nuovo metodo per permettere l'invocazione del metodo Calcula-tor.getLength()

public int getLength(String param) throws BusinessDelegateException {
  try {
    return this.calculator.getLength(param);
  } catch (RemoteException re) {
    throw new BusinessDelegateException(re);
  }
}

Le problematiche di localizzazione sono demandate alla classe MyServiceLocator che va op-portunamente configurata per localizzare l'EJB Calculator installato su WLS.
Per fare questo è sufficiente aggiungere una nuova configurazione nel file <<MO-KA_INTEGRATION>>/config/locator.xml che popolerà lo slot 1 dell'array businessServices della classe MyServiceLocator specificando la classe ContextFactory di Weblo-gic(weblogic.jndi.WLInitialContextFactory) e l'URL del server (t3://<SERVER>:7001):

<void index="1">
<object class="it.mokabyte.eai.my.locator.ServiceConfig">
<void property="homeClass">
<class>it.mokabyte.eai.other.ejb.CalculatorHome</class>
</void>
<void property="initialContextFactory">
<string>weblogic.jndi.WLInitialContextFactory</string>
</void>
<void property="jndiName">
<string>MokaCalculator</string>
</void>
<void property="providerUrl">
<string>t3://localhost:7001</string>
</void>
</object>
</void>

e aggiugere la relativa costante che identifica l'ID del servizio

public class Defines {

/** Service ID dell'EJB Calucalator installato su Weblogic */
public final static int CALCULATOR_SERVICE = 1;

Per potere utilizzare l'EJB Concat bisogna inserire gli opportuni JAR nella directory <TOM-CAT_HOME>\webapps\app_a\WEB-INF\lib.
Oltre quindi ai JAR che permettono la comunicazione tra Tomcat e l'EJB CalculatorBean in-stallato su JBoss (jboss.jar,jboss-j2ee.jar, jnpserver.jar ed il jar moka_ejb_integration.jar) biso-gna inserire i JAR per comunicare con Weblogic (weblogic.jar) e gli stub dell'EJB (ejb_wls_calculator.jar).
Con Tomcat 3, il JDK1.3 e WLS 6, il weblogic.jar doveva essere inserito in TOMCAT\lib e non in WEB-INF\lib della WebApp per problemi di classloader della versione 3 di Tomcat. Con Tomcat 4, il JDK 1.3 e WLS 6 il tutto funziona corretamente posizionando weblogic.jar nella WebApp\WEB-INF\lib.
Con Tomcat 4, il JDK1.4 e WLS 7, inserendo weblogic.jar in WebApp\WEB-INF\lib si ha un conflitto di classi tra le classi di Weblogic e quelle del JDK1.4. In fase di startup di Tomcat si ottiene una ServletException causata da una ClassCastException generata dalla classe ja-vax.xml.parsers.DocumentBuilderFactory.
Se invece si posiziona il weblogic.jar in TOMCAT\common\lib le classi del jar non vengono caricate per un bag delle prime versioni di Tomcat 4 (sia con la versione 4.01 che con la 4.03):

javax.naming.NoInitialContextException:
Cannot instantiate class: weblogic.jndi.WLInitialContextFactory. Root exception
is java.lang.ClassNotFoundException: weblogic.jndi.WLInitialContextFactory
at org.apache.catalina.loader.WebappClassLoader.
loadClass(WebappClassLoader.java:1307)...

Dati questi problemi, come è possibile procedere ?
Per il funzionamento su Tomcat 4.01 e 4.03 si può "alleggerire" il weblogic.jar provvedendo a includere i package strettamente necessari per comunicare con il protocollo T3 o togliendo quantomeno le classi di conflitto con il JDK 1.4 (javax/xml e org/xml) e rinpacchettando il tut-to in un nuovo jar. A questo punto il jar ottenuto può essere messo in webApp\WEB-INF\lib senza problemi di conflitti tra classi.
Se invece si vuole utilizzare il weblogic.jar "ufficiale" bisogna inserire il jar nella directory common\lib di Tomcat utilizzando release di Tomcat 4 piu' recenti.
Nella versione 4.1.18 di Tomcat il weblogic.jar di WLS 7 funziona correttamente se viene in-serito nella Tomcat\common\lib ma non viene caricato correttamente se inserito nella we-bApp\WEB-INF\lib. Il jar contenente la Home e Remote interface ed i relativi stub del Calcu-lator EJB si possono inserire nella directory WEB-INF\lib senza problemi di caricamento.
Una volta inserite le stringhe da concatenare nella pagina concat.jsp dell'Applicazione A, si otterrà come risultato sia la concatenazione che il calcolo della lunghezza della stringa conca-tenata:

<%
String resultConcat = null;
String resultLength = null;
try {
MyBusinessDelegate proxy = new MyBusinessDelegate();
resultConcat = proxy.getConcat( (String)request.getParameter("FIRST_STRING"),
(String)request.getParameter("SECOND_STRING"));
resultLength = "" + proxy.getLength(resultConcat);
}catch(Exception e){
resultLength = "-1";
}
%>

per poi visualizzare il risultato

<table width="400" border="0">
<tr>
<td>
Il risultato è <b><%= resultConcat %></b> di lunghezza <b>
<%= resultLength %></b>
</td>
</tr>
</table>

Abbiamo visto come permettere all'applicazione A di interagire oltre che con il servizio di concatenazione (l'EJB ConcatBean installato su JBoss) anche con il servizio di calcolo della lunghezza della stringa (l'EJB CalculatorBean installato su WLS) in modo diretto facendo convivere l'ORB di JBoss con quello di Weblogic.
In questo modo i due servizi sono invocati mediante un'"unica" navigazione, quella dell'applicazione A.
I due servizi sono invocati dal Business Delegate all'interno del Presentation tier dell'applicazione A dato che i servizi restituiscono dati primitivi e non hanno bisogno di nes-suna logica di business aggiuntiva né di elaborazioni dati (adattamento,trasformazione, norma-lizzazione, …).
In casi piu' "reali" capiterà che il servizio da integrare restituisca un proprio object model, (cioè dei propri Value Object - vedi [MOKA_VO]) che quindi occorre trasformare nei Value Object del dominio integrante. Inoltre è possibile che una volta ottenuti i dati si debbano effet-tuare elaborazioni di business aggiuntive.
E' quindi opportuno portare tale logica in un EJB Facade sul business tier esponendo un'interfaccia di business uniforme ed un modello dati omogeneo.


Figura 4
- Integrazione Object/RPC con EJB Facade

Per fare questo è opportuno sviluppare un EJB di "facciata" che si occupa di invocare i servizi di concatenazione e di calcolo di lunghezza e di implementare l'eventuale logica di business aggiuntiva (nel semplice esempio proposto si antepone alla stringa concatentata la data di ese-cuzione del servizio) e le eventuali operazioni di gestione dei dati (nel nostro semplice caso si trasforma l'intero in un Integer).

public class FacadeBean implements SessionBean {
. . .
public ResultOM getResult(String a, String b) throws RemoteException {
  try{
    ResultOM result = new ResultOM();
    // Richiedo la concatenazione all'EJB Concat installato su JBoss
    Properties properties = new Properties();
    properties.put(Context.INITIAL_CONTEXT_FACTORY,                    "org.jnp.interfaces.NamingContextFactory");
    properties.put(Context.PROVIDER_URL, "jnp://localhost:1099");
    Context ctx = new InitialContext(properties);
    Object ref = ctx.lookup("java:comp/env/ejb/Concat");
    ConcatHome home = (ConcatHome)     PortableRemoteObject.narrow(ref,ConcatHome.class);
    Concat concat = home.create();
    String strResult = concat.concat(a,b);
    // Richiedo il calcolo della lunghezza della stringa all'EJB   
    // Calculator installato su WLS
    properties.put(Context.INITIAL_CONTEXT_FACTORY,                    "weblogic.jndi.WLInitialContextFactory");
    properties.put(Context.PROVIDER_URL, "t3://localhost:7001");
    Context ctx = new InitialContext(properties);
    Object ref = ctx.lookup("MokaCalculatorWLS");
    CalculatorHome home = (CalculatorHome)     PortableRemoteObject.narrow(ref,CalculatorHome.class);
    Calculator calc = home.create();
    Integer iLength=calc.getLength(strResult);
    // Eseguo la logica di business "aggiuntiva"
    result.setConcat(new java.util.Date().toString()+":"+strResult);
    result.setLength(new Integer(iResult));
    // Ritorno il risultato
    return result;
  } 
  catch(Exception e){
    ...
  }

In questo modo il Business Delegate vede un unico punto d'accesso al Business tier (l'EJB Fa-cade installato su JBoss) e deve conoscere un unico object model (il ValueObject di classe Re-sultOM):



import it.mokabyte.eai.my.ejb.FacadeHome;
import it.mokabyte.eai.my.ejb.Facade;

public class MyBusinessDelegate{

private Facade facade = null;
. . . . .

public ResultOM getResult(String param1,String param2)
                throws BusinessDelegateException {
  try {
    FacadeHome home =(FacadeHome)
               MyServiceLocator.getHome(Defines.FACADE_SERVICE);
    this.facade= home.create();
    return this.facade.getResult(param1,param2);
    ...

Per il corretto funzionamento di quest'ultimo scenario presentato bisogna inserire weblogic.jar e il jar delle interfacce Home e Remote ed i relativi stub del Calculator EJB in <JBOSS_HOME>\server\all\lib; in Tomcat WebApp\WEB-INF\lib invece sono necessari i jar per comunicare con JBoss e le interfacce Home e Remote dell'EJB Facade.
Operazioni di "convivenza" di ORB non sono da sottovalutare. Si pensi ad esempio se al po-sto di Weblogic avessimo dovuto comunicare con un EJB installato su WAS (Websphere Ap-plication Server), l'Application Server di IBM: le cose si complicherebbero.
Websphere utilizza il JDK IBM che non è compatibile con quello SUN. Al fine di permettere la interoperabilità tra il JDK SUN di Tomcat e/o JBoss e quello IBM di WAS, bisogna installa-re il Pluggable Application Client [PAC] di IBM. L'installazione del PAC comporta essen-zialmente l'inclusione di 18 jar (10 MB da inserire nel classpath), la configurazione di una dozzina di proprietà di sistema e la modifica del file di policy di sicurezza del JRE/JDK.
Si può intuire come l'utilizzo di tale ORB diventa piu' complesso o quantomeno piu' "delicato".

 

Conclusioni
In questo articolo si è visto come è possibile integrare le due applicazioni J2EE (A,B) a livello Object/RPC e tutte e tre le applicazioni (A,B,C) a livello d'interfaccia utente.
Nel prossimo articolo si vedrà come è possibile sfuttare l'interoperabilità offerta dal protocollo IIOP per fare comunicare client CORBA con EJB e client Java con CORBA Server.


Allegati
Gli esempi sono disponibili qui

 

Bibliografia
[INT1] S.Rossini: Integrazione di applicazioni Enterprise (I) MokabyteN.71-Aprile 2003
[MOKA_VO] S.Rossini:"Value Object", Mokabyte N.69-Dicembre 2002
[MOKA_FAC] S.Rossini:"SessionFacade",Mokabyte N.64-Giugno 2002
[MOKA_ADP] L.Bettini:"Il pattern Adapter",Mokabyte N.33-Settembre 1999
[PAC] http://www7b.software.ibm.com/wsdd/downloads/pluggableclient.html

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