mercoledì 23 gennaio 2008

Capitolo 6 - Costruzione di una Interfaccia Client-Server

Introduzione

Vediamo ora come possiamo implementare un modulo che possa agire da interfaccia di collegamento tra la porzione del Controllore da far eseguire sul Client e la porzione residente sul WebServer che agisce da trade-union con un eventuale Database Server.

In teoria vorremmo poter avere a disposizione un modulo di interfaccia che possa rendersi indipendente il piu' possibile dalla particolare tecnologia con cui potremmo voler costruire la porzione di Controllore residente sul webserver.

Di modo che possa andar bene sia utilizzando ASP.NET/C# su un server IIS che JSP o PHP su un server Apache.

Vedremo quindi come sara' possibile adottare una architettura comune a tutte le tecnologie, specificando anche le peculiarita' necessarie nel caso si usi c# oppure jsp.


Il nostro modulo di interfaccia deve necessariamente avere una porzione lato client che dovra' intergarsi con il Controllore locale scritto in Javascript, ed una porzione lato server che deve integrarsi con la tecnologia server utilizzata.

Lato client si avra' sostanzialmente una sezione di API javascript aggiuntive da aggiungere al controller della nostra web application. Lato server si avra' una pseudo-pagina web dinamica nella tecnologia utilizzata che agira' come wrapper per la libreria-controller lato server della nostra web application.

Lato client

Come abbiamo visto nel capitolo 4 utilizzeremo il metodo net.ContentLoader per effettuare le richieste asincrone verso il webserver.

Il comando base sara' nella forma:

var loaderx=new net.ContentLoader('proxy.jsp?var1=value1&par2=value2’,handler,null,"POST",'');

ove si avra' una pagina dinamica (qui in jsp come esempio) cui passare eventualmente i parametri necessari da elaborare per produrre i dati da inviare in risposta a questa richiesta che verranno gestiti dalla funzione handler() del controller lato client.

Certo e' che e' molto scomodo utilizzare ogni volta la forma base per effettuare le varie richieste che dovremo effettuare verso il server.

Vediamo ora come migliorare il tutto costruendo intorno alla chiamate base una interfaccia di programmazione, o API.

Costruzione di una API lato client

Volendo strutturare un po' il nostro controller lato server possiamo raggruppare tutte le funzioni che ci possono servire in gruppi omogenei, ad esempio raggruppandoli per servizio lato client gestito.

Generalmente si suppone di strutturare l'interfaccia grafica in sezioni tra loro distinte ancorche' correlate, magari corrispondenti alle varie voci che possono presentarsi su un menu' introduttivo. In questo modo possiamo raggruppare i metodi implementati sul controller lato server per classi o servizi di appartenenza.

Grazie a cio' potremo suddividere l'implementazione del controller stesso in moduli separati legati tra loro dalla porzione dell'interfaccia che agira' come smistatore delle chiamate ricevute ai vari moduli.

Definiamo quindi il concetto di classe del nostro controller lato server come raggruppamento delle funzioni implementate per un determinato servizio. Quando faremo una richiesta al server quindi speficicheremo la classe di appartenenza e la funzione effettivamente richiamata.

In questo modo potremo utilizzare un comando simile, in una forma piu' comunemente utilizzata:

executeRequest(‘className’,’functionName’,’parameterList’,returnHandler);

Rimane da ricostruire di volta in volta la parte di querystring relativa al passaggio dei parametri.

Per rendere piu' agevole questo passo utilizziamo un meccanismo di push dei parametri in una struttura buffer costruita con un Array: creaimo un array in cui depositare i vari parametri che poi verranno recuperati dal costruttore della executeRequest che automaticamente generera' la querystring da passare al metodo net.ContentLoader.

var myp=new pars();
function pars(){
this.data=new Array();
}
pars.prototype.init=function(){
this.data.length=0;
}
pars.prototype.addPar=function(obj){
if(obj==undefined){
obj="";
}
this.data.push(obj);
};
pars.prototype.add3Par=function(){
this.addPar();
this.addPar();
this.addPar();
}
pars.prototype.remPar=function(p){
var len=this.data.length;
for(i=0;i<len;i++){
if(this.data[i]==p){
break;
}
}
var len1=len-1;
for(j=i;j<len1;j++){
this.data[j]=this.data[j+1];
}
this.data.length=this.data.length-1;
}
pars.prototype.remAll=function(){
this.data.length=0;
}
pars.prototype.shows=function(){
var output="";
var len=this.data.length;
for(i=0;i<len;i++){
output+=this.data[i].toString();
}
return output;
}

//common vars
lib='./proxy.jsp',bcNm="?class=",bcFx="&function="

function executeRequest(){
var a=executeRequest.arguments;
var clx=a[0]; //class name
var func=a[1]; //function name
var handler=a[2]; //return handler
var debug=a[3]; //debug truefalse
var sync=a[4]; //sync request truefalse
var vars="";
var len=myp.data.length;
var output="";
for(i=0;i<len;i++){
vars+="&value"+(i+1)+"="+myp.data[i].toString();
output+=myp.data[i].toString();
}
window.status=output;
var loaderx=new net.ContentLoader(lib+strParamsx,handler,null,"POST",'','','','',sync);
}


Cosi' per effettuare una richiesta remota occorre inizializzare il buffer dei parametri, aggiungere i parametri , ed effettuare la chiamata alla nostra funzione:


try{
myp.init();
myp.addPar("101");
exQuery("MacroDPDV","getCampi",retHandler);
}catch(err){
errmsgxxx('Preparing getNPWIP',err.message);
}

function retHandler(){
var d=this.req.responseText;
try{
alert(d);
}catch(err){
errmsg('Function retHandler',d+";"+err.message);
}
};

Interfaccia proxy lato server
Vediamo quali requisiti occorre soddisfare rispetto alla creazione e all'utilizzo del file dinamico di interfaccia da costruire sul webserver utilizzando una delle tecnologie a disposizione - JSP o ASP.NET/C#.
Mostriamo quindi un esempio di codice per queste due tecnologie nell'ottica di poterci interfacciare poi ad un insieme di librerie che si possano sviluppare a livello di Business Logic residente sul webserver per il collegamento eventuale con un Database server.
In sostanza vogliamo poter utilizzare per la tecnologia JSP un insieme di JavaBeans, ovvero una libreria scritta in Java, mentre per la tecnologia ASP.NET vorremmo poter collegarci ad una libreria web .NET scritta in C#.
Proxy proxy.jsp

<%@ page language="java" import="java.sql.*, java.util.*, pie.beans.fouryp.FourYPCapexVO"%>
<jsp:useBean id="setPropertyTomcat" scope="session" class="jsputilityservlet.SetPropertyServlet" />
<jsp:useBean id="db" scope="session" class="pie.db.DBConn" />
<jsp:useBean id="ExplGameOpPrj" scope="session" class="pie.beans.pop.ExplGameOpPrj" />
<jsp:useBean id="ExplActDAO" scope="session" class="pie.beans.action.ExplActDAO" />

<%

String beanName = request.getParameter("class");
String beanMethod = request.getParameter("function");
String value = request.getParameter("value1");
String value2 = request.getParameter("value2");
String value3 = request.getParameter("value3");
String result="";
int res=0;

final String newline = System.getProperty("line.separator");

try
{

if(beanName.equals("JSON")){
if(beanMethod.equals("getTechPrjNameList")){
System.out.println("JSON list of Projects Names");
result = ExplActDAO.getPrjNameListJSON(nav.getUser_id_nbr(),nav.getConnection());
out.println(result);
}
}
else if(beanName.equals("actionFilters")){
if(beanMethod.equals("setRefDate")){
ExplGameOpPrj.setExpl_ref_date(value);
}else
if(beanMethod.equals("setEndDateTo")){
ExplGameOpPrj.setAct_end_date_to(value);
}

}
else if(beanName.equals("Budget"))
{

}
}
catch(Exception e)
{
out.println("Errore:"+e.getMessage());
}

%>


In questo modo e' sufficiente includere i JavaBeans e le classi necessarie appartenenti alla libreria (magari compattata in un file jar) e definire le associazioni servizio/funzione/parametri passate dal client con le operazioni richieste ai Business Components del server e i parametri aggiuntivi necessari, eventualmente ricostruendo un messaggio o un export di output HTML o in formato JSON da restituire al client come risposta, nel modo classico per le pagine JSP.

Proxy proxy.ashx

Vediamo ora nel caso volessimo utilizzare la tecnologia .NET lato server.
Innanzitutto vorrei puntualizzare una cosa: lo scopo e' quello di poter utilizzare una libreria centralizzata lato server, che nel caso .NET si traduce nella compilazione di una DLL di classi C# piu' che nella creazione di pagine dinamiche .aspx, anche perche' sarebbe complicato riutilizzarla direttamente con una modalita' simile a quella vista per le librerie Java importabili come JavaBeans.

Occorre innanzitutto utilizzare una pagina dinamica del tipo .ashx , ovvero dobbiamo usare un ASP.NET Web Handler File che permette di richiamare un HTTP Handler.

Un HTTP Handler non è altro che una classe .NET che implementa l'interfaccia IHttpHandler (reperibile sotto il namespace System.Web). Tale interfaccia espone un metodo utile a processare la richiesta HTTP in entrata (il metodo ProcessRequest) e una proprietà, di tipo boolean, utile a decidere se l'istanza dell'HTTP Handler corrente può essere utilizzata o meno da altre richieste (la proprietà IsReusable). Chiaramente, è proprio attraverso l'implementazione di questa interfaccia che noi possiamo sviluppare gli handler utili a svolgere le funzionalità custom di cui abbiamo bisogno.

Il nostro file proxy.ashx contiene solo l'indicazione dell' HTTP Handler:


<%@ WebHandler Language="C#" class="Library.Dispatch"  codebehind="Dispatch.cs" %>

In questo modo si richiama il cotruttore della classe Dispatch compilata nel file Library.dll In questo modo poi la classe Dispatch instrada le associazioni servizio/funzione richieste passando i parametri necessari alle varie funzioni implementate in classi separate per rispecchiare i vari servizi implementati sul client.


using System;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using Oracle.DataAccess.Client;

namespace Library
{

public class Dispatch : IHttpHandler, IRequiresSessionState
{
internal OracleConnection conn=new OracleConnection();
internal OracleCommand cmd=new OracleCommand();
internal string connString="";

private HttpContext ctx;
public OracleConnection connTmp;
Library.LoginHandler lh;
Library.postLogin pl;
Library.Servizio1 s1;
Library.Servizio1 s2;

public Dispatch()
{
conn.ConnectionString="";
}
public void ProcessRequest(HttpContext ctx)
{

try
{
if(ctx.Request["class"]=="login")
{
lh=new LoginHandler();
lh.ProcessRequest(ctx);
}
else if(ctx.Request["class"]=="postLogin")
{
pl=new postLogin();
pl.ProcessRequest(ctx);
}
else if(ctx.Request["class"]=="Servizio1")
{
s1=new PerseoStogit.PozziCampione();
if(ctx.Request["function"]=="Funzione1")
{
s1.Funzione1();
}
else if(ctx.Request["function"]=="Funzione2")
{
s1.Funzione2();
}
}
else if(ctx.Request["class"]=="Servizio2")
{
s2=new PerseoStogit.userServices();
if(ctx.Request["function"]=="Funzione1")
{
s2.Funzione1();
}
if(ctx.Request["function"]=="Funzione2")
{
s2.Funzione2();
}
}
else
{
ctx.Response.Write("Non e' stata trovata una corrispondenza con la classe e la funzione chiamate: "+ctx.Request.QueryString.ToString());
}
}
catch(Exception err)
{ctx.Response.Write("Invalid Operation - call system administrator"+err.Message.ToString());}
}
public bool IsReusable { get { return false; } }
}
}


In questo caso e' obbligatorio implementare in ogni classe della libreria i metodi ProcessRequest(HttpContext ctx) e IsReusable specificando che la classe implementa le interfacce IHttpHandler e IRequiresSessionState.
Sono comunque dettagli inerenti alla tecnologia .NET e rimandiamo alla documentazione dedicata per gli approfondimenti necessari. Poi ogni classe c# implementera' le funzionalita' richieste restituendo il risultato al client utilizzando il metodo ctx.Response.Write(...) associato al contesto http gestito dall' HTTP Handler.

Riassunto

In questo modo, seguendo questa architettura, e' possibile interfacciare una libreria di Business Components lato server scritta pressoche' in qualsiasi linguaggio web dinamico (magari in PHP anche se non so se sia possibile costruire un modulo unificato come puo' essere un unico file jar per i JavaBeans o un unica dll compilata per l' http handler .net) oltre a quelli illustrati a patto di costruire una pagina proxy opportuna, potendo utilizzare le stesse API client di interfaccia.

Nessun commento: