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
|