You are on page 1of 51

7 Implementarea aplicaiilor distribuite pe platformele .

NET i J2EE
Aplicaiile economice vehiculeaz un volum mare de date care trebuie prelucrate n vedere obinerii de informaii necesare pentru fundamentarea procesului decizional. Globalizarea la nivel microeconomic a activitii a transformat treptat modelul de prelucare client-server specific primelor aplicaii de reea, ntr-un model bazat pe niveluri de servicii, cel mai simplu incluznd nivelul de acces la date, nivelul logicii aplicaiei i nivelul de prezentare prin care aplicaia este accesat de ctre utilizatori. .NET este soluia propus de Microsoft pentru programarea aplicaiilor distribuite n Internet, fiind identificat prin urmtoarele trei componente eseniale: un mediu de execuie independent de limbaj optimizat pentru prelucrri distribuite .NET Framework; un mediu de dezvoltare care ofer suport pentru mai multe limbaje de programare standardizate sau proprietate Microsoft Visual Studio .NET; sistemul de operare care ofer suport pentru aplicaiile distribuite dezvoltate pe platforma .NET Framework Windows Server. Viziunea care a stat la baza iniiativei .NET a avut n vedere asigurarea urmtoarelor deziderate: programarea independent de limbaj; robusteea i scalabilitatea aplicaiilor pe ansamblu; securitatea integrat; uurina implementrii; distribuirea prelucrrilor; suportul pentru standarde deschise; mentenabilitatea; faciliti de depanare avansate. .NET este similar platformei J2EE; ambele constituie o abordare structurat pentru crearea aplicaiilor distribuite, oferind limbaje compilate n cod intermediar mpreun cu o bogat colecie de interfee de programare pentru dezvoltarea aplicaiilor. Intenia platformei Java a fost s ofere suport pentru aplicaiile scrise n limbajul Java i compilate n cod de octei Java. Chiar dac au existat ncercri de a porta i alte limbaje pe platforma JVM, ele au rmas doar n sfera limbajelor formale, Jython fiind doar un exemplu

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

didactic de portare a limbajului de scripting Python pe JVM, i asta pentru c ideea platformei Java a fost mereu una simpl i eficient: un singur limbaj care s ruleze pe mai multe sisteme de operare. Spre deosebire de J2EE, platforma .NET ofer suport pentru mai multe limbaje de programare compilate n Microsoft Intermediate Language (MSIL), plecnd de la ideea: o singur platform pe care pot rula mai multe limbaje. Firma Sun Microsystems a dezvoltat Java att ca limbaj, ct i platform pentru dezvoltarea aplicaiilor distribuite, existnd n prezent trei ediii ale platformei Java: J2SE (Java 2 Standard Edition), pentru programarea de platform i n reea; J2EE (Java 2 Enterprise Edition), pentru aplicaii de ntreprindere; J2ME (Java 2 Micro Edition), pentru dispozitive mobile. Spre deosebire de .NET, J2EE este un set de specificaii standardizate i nu un produs, permind dezvoltatorilor s creeze aplicaii distribuite pe mai multe niveluri. Diferenele fundamentale ntre J2EE i .NET se concretizeaz n: suportul pentru sisteme de operare; suportul pentru limbajele de programare; metoda de execuie. nc de la nceput Java a fost proiectat s lucreze cu un numr mare de sisteme de operare, rulnd n prezent pe platforme: Windows, UNIX, Linux, MacOS, BeOS. Spre deosebire de J2EE, .NET a fost gndit s ruleze doar pe Windows, existnd ns ncercri de portare a mediului de execuie i pe Linux, proiectele de tip Open Source, precum Mono i Rotor oferind suport pentru majoritatea interfeelor de programare din .NET. Pentru implementarea aplicaiilor distribuite este nevoie de servere de aplicaii pentru publicarea componentelor care nglobeaz logica aplicaiei. Paltforma .NET ofer IIS cu ASP.NET prin intermediul cruia se pot publica i executa servicii Web scrise n limbaje compilabile pe CRL (Common Language Runtime) n MSIL (Microsoft Intermediate Language), cum sunt C#, J#, C++ Managed, Pascal (Delphi), Visual Basic .NET, Perl etc., sau chiar Java prin IkVm i Mono; Platforma J2EE dispune att de implementri comerciale, precum WebLogic de la BEA, WebSphere Application Server de la IBM, iPlanet i SunONE Application Server de la Sun, Enterprise

Reele de calculatoare

Server de la Borland etc., precum i de implementri Open Source cum sunt JBoss sau JOnAs. Acestea sunt servere de componente EJB (Enterprise Java Beans), care, pentru a putea gzdui servicii Web, necesit, ca i IIS-ul, un mediu de procesare a mesajelor SOAP i de gestiune a componentelor de serviciu. n J2EE exist mai multe implementri SOAP, precum Axis i SOAP de la Apache, Web Services Developement Kit (WSDK) de la Sun i omonimul su de la IBM, sau Glue, iniial dezvoltat de The Mind Electric, actualmente oferit de WebMethods. Pentru accesul la date, aplicaiile distribuite fac apel la servere de baze de date relaionale, prin interfee de programare specifice. Platform .NET ofer ActiveX Database Objects .NET (ADO.NET), prin intermediul creia se poate conecta la server Microsoft precum SQL Server 2000 (sau Yukon n curnd), MSDE, Access, dar i la Oracle Database Server, IBM DB2, precum i la MySQL sau PostGre SQL. Platforma J2EE dispune de Java Database Connectivity (JDBC), o interfa de programare pentru care exist drivere de conectare la aproape orice baz de date relaional sau sistem de fiiere, incluznd Oracle Database Server, IBM DB2, Sybase, MS SQL Server 2000, MSDE, MS Access, MySQL, PostGre SQL etc. Pentru clieni Web este nevoie de servere Web cu suport dedicat tehnologiilor specifice, astfel: Platforma .NET ofer ASP.NET, o tehnologie dezvoltat din ASP n sensul separrii procesrilor pe server de formatarea HTML a rezultatului, permind o mai bun ncapsulare a codului n executabile binare interpretabile pe CLR. Necesit IIS cu modul ISAPI ASPNET.dll sau Apache cu modul dedicat ASP.NET. Platforma J2EE ofer dou tehnologii: servlet i JSP, crora li s-a adugat recent JSF (Java Server Faces), o tehnologie echivalent cu ASP.NET. Se execut att pe servere Web, cele mai folosite fiind Tomcat, Orion sau Jetty, ct i pe servere de aplicaii J2EE amintite la nivelul logicii aplicaiei.

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

7.1 Nivelul de acces la date 7.1.1 Apelarea procedurilor stocate pe SQL Server Microsoft SQL Server 2000 reduce vizibil timpul necesar pentru a construi soluii bazate pe Web, de afaceri i de depozitare a datelor, automatiznd n acelai timp sarcinile de gestionare i adaptare. SQL Server 2000 este optimizat pentru a fi folosit cu Windows 2000 i este proiectat pentru a crete o dat cu afacerea dumneavoastr. Caracteristicile pentru a aduce pe Web aplicaiile noi i existente sunt incluse cu suport XML i acces HTTP. Alte caracteristici includ capacitatea de cutare de text n bazele de date i formate de documente cunoscute; auto-gestionare i adaptare dinamic, servicii de analiz integrate i migrarea i transformarea simplificat a datelor. Procedurile stocate sunt o colecie de declaraii precompilate stocate sub un nume i procesate ca o unitate. Procedurile stocate sunt disponibile pentru administrarea SQL Server i afiarea informaiilor despre baze de date i utilizatori. Procedurile stocate instalate o dat cu SQL Server se numesc proceduri stocate de sistem. Cnd se creeaz o aplicaie cu SQL Server, limbajul de programare Transact-SQL (TSQL) este interfaa primar de programare ntre aplicaie i baza de date SQL Server. Manipularea i execuia procedurilor stocate se poate realiza n dou moduri: prin stocarea local a programelor i crearea aplicaiilor care s trimit comezile ctre SQL Server i s proceseze rezultatele; prin stocarea programelor ca proceduri stocate n SQL Server i crearea aplicaiilor care s execute aceste proceduri i s proceseze rezultatele. Procedurile stocate din SQL Server sunt similare procedurilor din alte limbaje deoarece pot s: primeasc parametri introdui i s returneze valori multiple sub forma parametrilor de ieire; conin declaraii care s ndeplineasc operaiuni n baza de date, inclusiv s cheme alte proceduri. Se poate folosi declaraia EXECUTE pentru a rula o procedur stocat. Procedurile stocate sunt diferite de funcii deoarece nu pot returna valori n locul numelui lor i nu pot fi folosite direct ntr-o expresie. Avantajele folosirii procedurilor stocate n SQL Server n locul programelor Transact-SQL stocate local n sistemele client sunt: permit programarea modular: procedura poate fi creat doar o singur dat, stocat n baza de date i chemat ori de cte ori este nevoie n program. Procedurile stocate pot fi create de o persoan

Reele de calculatoare

specializat n programarea bazelor de date i pot fi modificate independent de codul surs al programului care le folosete; permit rularea mai rapid: dac operaiunea cere o cantitate mare de cod Transact-SQL sau este rulat n mod repetat, procedurile stocate pot fi mai rapide dect codul Transact-SQL de la client. Ele sunt analizate i optimizate n momentul crerii lor i versiunea in-memory a procedurii poate fi folosit dup ce este executat pentru prima dat; permit reducerea traficului pe reea: o operaiune care cere sute de linii de cod Transact-SQL poate fi executat printr-o singur declaraie care execut codul n cadrul unei proceduri, dect s se trimit sute de linii de cod pe reea. Pentru apelarea unei proceduri stocate, clientul ADO.NET pentru SQL Server ofer obiectul SqlCommand pentru care se seteaz proprietatea CommandType la valoarea StoredProcedure, respectiv CommandText ca fiind numele procedurii stocate din spaiul rolului conexiunii deschise, astfel:
this.sqlCommand = new SqlCommand(); this.sqlCommand.CommandText = "dbo.[getProducts]"; this.sqlCommand.CommandType = CommandType.StoredProcedure; this.sqlCommand.Connection = this.sqlConnection; this.sqlCommand.Parameters.Add(new SqlParameter("@RETURN_VALUE", SqlDbType.Int, 4, ParameterDirection.ReturnValue, false, ((byte)(0)), ((byte)(0)), "", DataRowVersion.Current, null));

n cazul unei proceduri stocate care selecteaz date din mai multe tabele prin intermediul unui adapter trebuie mapate coleciile extrase din baz peste tabelele DataSet-ului destinaie:
this.sqlDataAdapter.SelectCommand = this.sqlCommand; this.sqlDataAdapter.TableMappings.Add("data", "Categories"); this.sqlDataAdapter.TableMappings.Add("data1", "Producers"); this.sqlDataAdapter.TableMappings.Add("data2", "Products"); this.sqlDataAdapter.TableMappings.Add("data3", "Stock");

procedura stocat selecteaz datele astfel:


CREATE AS SELECT SELECT SELECT SELECT RETURN PROCEDURE dbo.getProducts * * * * FROM FROM FROM FROM Categories; Producers; Products; Stock;

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

apelul seleciei datelor cu ajutorul adapter-ului presupune denumirea tabelei origine ale crei mapri duc datele n tabele ale DataSet-ului:
DataSet dataSet = new DataSet(); this.sqlDataAdapter.Fill(dataSet, "data");

JDBC ofer obiectul CallableStatement prin intermediul cruia se pot apela proceduri stocate. Obiecte Java
package tutorials.database.storedprocedure; import java.io.Serializable; public class Group implements Serializable { public int GroupId; public String Name; public short Version; } package tutorials.database.storedprocedure; import java.io.Serializable; public class Student implements Serializable { public int StudentId; public String FirstName; public String LastName; public short Version; }

Accesor
package tutorials.database.storedprocedure; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Vector; public class DataAccessor { private Connection connection; public void open(String host, int port, String database, String user, String password) throws SQLException, ClassNotFoundException { Class.ForName("com.microsoft.jdbc.sqlserver.SQLServerDri ver"); this.connection = DriverManager.getConnection(

Reele de calculatoare "jdbc:microsoft:sqlserver://" + host + ":" + port + ";DatabaseName=" + database + ";User="+ user + ";Password=" + password + ";");

} public void close() throws SQLException { this.connection.close(); } public Group[] getGroups() throws SQLException { Vector vector = new Vector(); CallableStatement statement = this.connection.prepareCall("call GetGroups()"); ResultSet results = statement.executeQuery(); while(results != null && results.next()) { Group group = new Group(); group.GroupId = results.getInt(1); group.Name = results.getString(2); group.Version = results.getShort(3); } return (Group[]) vector.toArray(); } public Student[] getStudentsByGroup(int groupId) throws SQLException { CallableStatement statement = this.connection.prepareCall("call GetStudentsByGroup(?)"); statement.setInt(1, groupId); Vector vector = new Vector(); ResultSet results = statement.executeQuery(); while(results != null && results.next()) { Student student = new Student(); student.StudentId = results.getInt(1); student.FirstName = results.getString(2); student.LastName = results.getString(3); student.Version = results.getShort(4); vector.add(student); } return (Student[]) vector.toArray(); } }

7.1.2 Virtualizarea accesului la baza de date Platforma .NET permite accesul la baze de date prin interfaa ADO.NET, oferind mecanisme de acces bazate pe conexiune activ sau deconectate. n privina suportului nativ pentru conectarea la diferite servere, platforma .NET ofer suport pentru SQL Server chiar de la versiunea 1.0 cruia i s-a

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

adugat suportul pentru Oracle n versiunea 1.1, restul serverelor fiind suportate prin intermediul provider-ilor oferii de diferite organizaii. Spre exemplu, pentru accesul la MySQL exist mai muli provider-i comerciali ct i Open Source, cel mai cunoscut fiind ByteFX. Dac n cazul punii JDBC difer doar protocolul de conectare n funcie de server, restul metodelor fiind comune, pe platforma .NET clasele de acces la date se gsesc n pachete diferite i au tipuri diferite, ceea ce face imposibil legarea direct la metodele acestora. Prin intermediul signaturii comune a metodelor interfeei ADO.NET pe care fiecare clas de acces o implementeaz, este totui posibil definirea unei interfee de acces la date, ce urmeaz a avea implementri specifice fiecrui provider, implementri care fac apel la pachetele i clasele provider-ului respectiv. Pentru ca aceeai aplicaie s se poat conecta la mai multe servere de baze de date este necesar virtualizarea accesului la baza de date prin implementarea unui mecanism de fabric de obiecte care s asigure legarea implementrii specifice bazei de date utilizate. se definete o interfa peste ADO.NET care reunete funcii pentru operaiile cu baza de date; este necesar definirea unei interfee deoarece clasele oferite de ctre provider-ii ADO.NET au signaturi de pachete diferite, ceea ce face imposibil instanierea lor pentru acelai tip de baz:
namespace Tutorials.Database { internal interface IDatabaseConnection { void Open(string host, string database, string user, string password); System.Data.DataSet ExecuteQuery(string statement, string table); object ExecuteScalar(string statement); int ExecuteNonQuery(string statement); void Close(); string Provider { get; } string Database { get; } } }

Reele de calculatoare

se implementeaz interfaa pentru fiecare server de baze de date utiliznd clasele provider-ului ADO.NET specific: Clas de acces la SQL Server 2000
namespace Tutorials.Database { internal class SQLServerDatabaseConnection: IDatabaseConnection { private System.Data.SqlClient.SqlConnection connection; private string database; public SQLServerDatabaseConnection() { } public void Open(string host, string database, string user, string password) { this.connection = new System.Data.SqlClient.SqlConnection("data source = " + host + "; database = " + database + "; user id = " + user + "; password = " + password); this.connection.Open(); } public System.Data.DataSet ExecuteQuery(string statement, string table) { System.Data.DataSet dataSet = new System.Data.DataSet(); new System.Data.SqlClient.SqlDataAdapter(statement, this.connection).Fill(dataSet, table); return dataSet; } public object ExecuteScalar(string statement) { return new System.Data.SqlClient.SqlCommand(statement, this.connection).ExecuteScalar(); } public int ExecuteNonQuery(string statement) { return new System.Data.SqlClient.SqlCommand(statement, this.connection).ExecuteNonQuery(); } public void Close()

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE {

} public string Provider { get { return "SQLServer"; } } public string Database { get { return this.database; } }

if(this.connection!=null) this.connection.Close();

Clas de acces la Oracle


namespace Tutorials.Database { internal class OracleDatabaseConnection: IDatabaseConnection { private System.Data.OracleClient.OracleConnection connection; private string database; public OracleDatabaseConnection() { } public void Open(string host, string database, string user, string password) { this.connection = new System.Data.OracleClient.OracleConnection("data source = " + host + "; database = " + database + "; user id = " + user + "; password = " + password); this.connection.Open(); } public System.Data.DataSet ExecuteQuery(string statement, string table) { System.Data.DataSet dataSet=new System.Data.DataSet();

Reele de calculatoare new System.Data.OracleClient.OracleDataAdapter(statement, this.connection).Fill(dataSet, table); return dataSet; } public object ExecuteScalar(string statement) { return new System.Data.OracleClient.OracleCommand(statement, this.connection).ExecuteScalar(); } public int ExecuteNonQuery(string statement) { return new System.Data.OracleClient.OracleCommand(statement, this.connection).ExecuteNonQuery(); } public void Close() { if(this.connection!=null) this.connection.Close(); } public string Provider { get { return "Oracle"; } } public string Database { get { return this.database; } } } }

Clas de access la MySQL


namespace Tutorials.Database { internal class MySQLDatabaseConnection: IDatabaseConnection { private ByteFX.Data.MySqlClient.MySqlConnection connection; private string database;

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE public MySQLDatabaseConnection() { } public void Open(string host, string database, string user, string password) { this.database = database; this.connection = new ByteFX.Data.MySqlClient.MySqlConnection(" data source = "+host+";database = "+database+";user id = "+user+";password = "+password); this.connection.Open(); } public System.Data.DataSet ExecuteQuery(string statement,string table) { System.Data.DataSet dataSet=new System.Data.DataSet(); new ByteFX.Data.MySqlClient.MySqlDataAdapter(statement,this.connec tion).Fill(dataSet, table); return dataSet; } public object ExecuteScalar(string statement) { return new ByteFX.Data.MySqlClient.MySqlCommand(statement, this.connection).ExecuteScalar(); } public int ExecuteNonQuery(string statement) { return new ByteFX.Data.MySqlClient.MySqlCommand(statement, this.connection).ExecuteNonQuery(); } public void Close() { if(this.connection != null) this.connection.Close(); } public string Provider { get { return "MySQL"; } } public string Database { get

Reele de calculatoare { } }

return this.database;

se construiete o clas fabric de obiecte care instaniaz implementarea interfeei de acces la baza de date specific unui anumit server: Clas fabric de conexiune la o anumit baz de date
using System; namespace Tutorials.Database { public enum Providers{Oracle, SqlServer, MySql} public class DatabaseConnection { private IDatabaseConnection databaseConnection = null; public void Open(Providers provider, string host, string database, string user, string password) { switch(provider) { case Providers.Oracle: this.databaseConnection = new OracleDatabaseConnection(); break; case Providers.SqlServer: this.databaseConnection = new SQLServerDatabaseConnection(); break; case Providers.MySql: this.databaseConnection = new MySQLDatabaseConnection(); default: this.databaseConnection = null; break; } if(this.databaseConnection != null) this.databaseConnection.Open(host, database, user, password); } public System.Data.DataSet ExecuteQuery(string statement, string table) {

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE return this.databaseConnection != null ? this.databaseConnection.ExecuteQuery(statement, table) : null; } public object ExecuteScalar(string statement) { return this.databaseConnection != null ? this.databaseConnection.ExecuteScalar(statement) : null; } public int ExecuteNonQuery(string statement) { return this.databaseConnection != null ? this.databaseConnection.ExecuteNonQuery(statement) : -1; } public void Close() { if(this.databaseConnection != null) { this.databaseConnection.Close(); this.databaseConnection = null; } } public string Provider { get { return this.databaseConnection != null ? this.databaseConnection.Provider : null; } } public bool IsOpened { get { return this.databaseConnection != null; } } } }

Spre deosebire de soluia ADO.NET n care fiecare provider ofer drivere avnd signaturi de pachet diferite i deci incompatibile ca tip de dat, JDBC ofer o interfa unitar de acces, ceea ce faciliteaz implementarea unei clase unice de acces la diferite baze de date. Totui, crearea unei conexiuni este specific fiecrui provider prin driverul i protocolul de acces aferente, ceea ce impune definirea unei clase abstracte avnd o singur metod abstract care creeaz o conexiune la baza de date, restul metodelor apelnd interfaa JDBC.

Reele de calculatoare

se definete o clas abstract care va conine metoda abstract de conectare i implementarea comun a metodelor de acces la date, de forma:
public abstract class DatabaseConnection { protected String host; protected int port; protected String database; protected String user; protected String password; protected java.sql.Connection connection; public abstract void connect(String host, int port, String database, String user, String password) throws java.lang.ClassNotFoundException, java.sql.SQLException; public void close()throws java.sql.SQLException { this.connection.close(); } public java.sql.ResultSet executeQuery(String statement) throws java.sql.SQLException { return(this.connection.createStatement()).executeQuery(s tatement); } public boolean executeInsert(String statement) throws java.sql.SQLException { return(this.connection.createStatement()).execute(statem ent); } public boolean executeUpdate(String statement) throws java.sql.SQLException { return(this.connection.createStatement()).execute(statem ent); } public boolean executeDelete(String statement) throws java.sql.SQLException { return(this.connection.createStatement()).execute(statem ent); } }

pentru conectarea la un anumit server se extinde clasa abstract prin implementarea metodei de conectare; spre exemplu, pentru conectarea la MySQL, clasa derivat va arta astfel:
public class MySQLDatabaseConnection extends DatabaseConnection { public MySQLDatabaseConnection() { }

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE public void connect(String host, int port, String database, String user, String password) trows java.lang.ClassNotFoundException, java.sql.SQLException { this.host = host; this.port = port; this.database = database; this.user = user; this.password = password; Class.forName("org.gjt.mm.mysql.Driver"); this.connection = java.sql.DriverManager.getConnection("jdbc:mysql://" + this.host + ":" + this.port + "/" + this.database + "?user=" + this.user + "&password=" + this.password); } }

pentru conectarea la SQL Server clasa derivat va arta astfel:


public class SQLServerDatabaseConnection extends DatabaseConnection { public SQLServerDatabaseConnection() { } public void connect(String host, int port, String database, String user, String password) throws java.lang.ClassNotFoundException,java.sql.SQLException { this.host = host; this.port = port; this.database = database; this.user = user; this.password = password; Class.forName("com.microsoft.jdbc.sqlserver.SQLServerDri ver"); this.connection = java.sql.DriverManager.getConnection("jdbc:microsoft:sqlserver ://" + this.host+ ":" + this.port + ";DatabaseName=" + this.database + ";User=" + this.user + ";Password=" + this.password + ";"); } }

n ambele cazuri metoda de conectare face apel la clasele punii JDBC pentru serverul de baze de date respectiv, astfel nct pentru deschiderea conexiunii prin JDBC este necesar plasarea claselor de acces n CLASSPATH.

Reele de calculatoare

7.2 Nivelul logicii aplicaiei 7.2.1 Apelarea procedurilor la distan Publicarea i apelarea unui obiect prin .NET Remoting presupune urmtoarele aciuni: se definete interfaa i obiectele de serviciu ntr-un assembly partajat ntre server i client, de forma: Obiect de serviciu
using System; namespace Tutorials.Remoting.Common { [Serializable] public class Customer { public string FirstName; public string LastName; public DateTime DateOfBirth; } }

Interfa serviciu
namespace Tutorials.Remoting.Common { public interface ICustomerManager { Customer[] GetCustomers(); } }

se construiete un proiect n care se implementeaz interfaa de apel:


using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using Tutorials.Remoting.Common; namespace Tutorials.Remoting.Service { public class CustomerManager : MarshalByRefObject, ICustomerManager {

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE public CustomerManager() { Console.WriteLine("CustomerManager.constructor: Object created"); } public Customer[] GetCustomers() { Customer[] customers = new Customer[2]; customers[0] = new Customer(); customers[0].FirstName = "Iulian"; customers[0].LastName = "Nemedi"; customers[0].DateOfBirth = new DateTime(1979, 7, 24); customers[1] = new Customer(); customers[1].FirstName = "Dan"; customers[1].LastName = "Cuculescu"; customers[1].DateOfBirth = new DateTime(1979, 1, 1); return customers; } } }

pentru publicarea serviciului se poate opta pentru un context Web pe IIS, un serviciu de platform sau o aplicaiei consol de test:
using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; using Tutorials.Remoting.Service; namespace Tutorials.Remoting.Server { class Server { [STAThread] static void Main(string[] args) { HttpChannel channel = new HttpChannel(1979); ChannelServices.RegisterChannel(channel); RemotingConfiguration.RegisterWellKnownServiceType( typeof(CustomerManager), "CustomerManager.soap", WellKnownObjectMode.Singleton); Console.ReadLine(); }

Reele de calculatoare }

clientul obine o referin la obiectul la distan apelnd activatorul canalului HTTP la serverul de obiecte, prin intermediul crei apeleaz metodele obiectului:
using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Channels; using Tutorials.Remoting.Common; namespace Tutorials.Remoting.Client { class Client { [STAThread] static void Main(string[] args) { HttpChannel channel = new HttpChannel(); ChannelServices.RegisterChannel(channel); ICustomerManager manager = (ICustomerManager) Activator.GetObject( typeof(ICustomerManager), "http://localhost:1979/CustomerManager.soap"); Console.WriteLine("Client.Main(): Reference to CustomerManager acquired"); Customer[] customers = manager.GetCustomers(); foreach(Customer customer in customers) Console.WriteLine("Customer:\n" + "First Name:\t" + customer.FirstName + "\n" + "Last Name:\t" + customer.LastName + "\n" + "Day of Birth:\t" + customer.DateOfBirth.ToString("dd\\.MM\\.yyyy") + "\n"); } } }

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

Arhitectura Java pentru apelarea procedurilor la distan prin RMI (Remote Method Invocation): Interfa de metode apelabile la distan
package tutorials.remoting; import java.rmi.Remote; import java.rmi.RemoteException; public interface IPrimeNumbers extends Remote { public int[] getPrimeNumbers(int n) throws RemoteException; }

Implementarea interfeei de metode apelabile la distan


package tutorials.remoting; import java.rmi.RemoteException; import java.rmi.server.UnicastRemoteObject; public class PrimeNumbers extends UnicastRemoteObject implements IPrimeNumbers { public PrimeNumbers() throws RemoteException { super(); } public int[] getPrimeNumbers(int n) throws RemoteException { boolean[] isNotPrime = new boolean[n + 1]; int found = 0; for(int i = 2; i <= n - 1; i++) if(isNotPrime[i] == false) { found++; for(int j = 2; j * i <= n; j++) isNotPrime[j * i] = true; } int[] numbers = new int[found]; int k = 0; for(int i = 2; i <= n; i++) if(isNotPrime[i] == false) numbers[k++] = i; return numbers; } }

Reele de calculatoare

Server pentru obiectul ale crui metode sunt apelabile la distan


package tutorials.remoting; import java.rmi.Naming; import java.rmi.RMISecurityManager; public class Server { public static void main(String[] args) { System.setSecurityManager(new RMISecurityManager()); try { PrimeNumbers primeNumbers = new PrimeNumbers(); Naming.rebind("//remoting/PrimeNumbersServer", primeNumbers); } catch(Exception exception) { System.out.println(exception.toString()); } } }

Client care apeleaz metode ale unui obiect la distan


package tutorials.remoting; import java.rmi.Naming; public class Client { public static void main(String[] args) { try { PrimeNumbers primeNumbers = (PrimeNumbers) Naming.lookup("rmi://remoting/PrimeNumbersService"); int n = args.length == 1 ? Integer.parseInt(args[0]) : 1000; int[] numbers = primeNumbers.getPrimeNumbers(n); String message = ""; for(int i = 0; i < numbers.length; i++) message += (message.length() > 0 ? ", " : "") + numbers[i]; System.out.println(message); } catch(Exception exception) { System.out.println(exception.toString()); } } }

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

7.2.2 Serializarea tipurilor de date complexe pentru transportul tipurilor de date complexe este necesar serializarea / deserializarea lor XML; mecanismul poate fi implementat declarativ prin intermediul atributelor sau imperativ prin instruciuni care apeleaz un obiect de tipul XmlWriter, respectiv XmlReader: Clas C# serializat XML n mod declarativ
using System; using System.Xml.Serialization; namespace Tutorials.ComplexTypeServices.Service { [XmlRoot(Namespace = "http://aris.ase.ro/schemas")] public class Contact { [XmlElement("FirstName")] public string FirstName; [XmlElement("LastName")] public string LastName; [XmlElement("Email")] public string Email; } }

Clas Java serializat XML n mod declarativ


package tutorials.services.complextype; import java.io.Serializable; public class Contact implements Serializable { public String FirstName; public String LastName; public String Email; }

serviciul expune o metod care ntoarce un vector de obiecte serializate XML: Serviciu C#
using System; using System.Collections; using System.ComponentModel;

Reele de calculatoare using System.Data; using System.Diagnostics; using System.Web; using System.Web.Services; namespace Tutorials.ComplexTypeServices.Service { [WebService(Namespace = "http://aris.ase.ro/schemas")] public class ContactService : System.Web.Services.WebService { public ContactService() { InitializeComponent(); } #region Component Designer generated code private IContainer components = null; private void InitializeComponent() { } protected override void Dispose( bool disposing ) { if(disposing && components != null) { components.Dispose(); } base.Dispose(disposing); } #endregion [WebMethod] public Contact[] GetContacts() { Contact[] contacts = new Contact[2]; contacts[0] = new Contact(); contacts[0].FirstName = "Iulian"; contacts[0].LastName = "Ilie-Nemedi"; contacts[0].Email = "iulian.nemedi@ase.ro"; contacts[1] = new Contact(); contacts[1].FirstName = "Radu"; contacts[1].LastName = "Constantinescu"; contacts[1].Email = "radu.constantinescu@ase.ro"; return contacts; } } }

ntr-un proiect Java dezvoltat cu ajutorul mediului Eclipse, se copiaz structura unei aplicaii Glue generate cu ajutorul utilitarului NewApp (copiat de fapt din Glue/app-template); se

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

precizeaz c binarele aplicaiei vor fi depuse n calea WEBINF/classes; se organizeaz clasele ntr-un director surs, respectiv ntr-un pachet Java n care se definete un tip complex care va fi ntors de metodele serviciului Web; pentru a putea fi serializat XML acesta trebuie s implementeze interfaa java.io.Serializable; se definete interfaa serviciului Web: Interfaa serviciului Java
package tutorials.services.complextype; public interface IContactService { public Contact[] getContacts(); }

se implementeaz interfaa prin intermediul clasei serviciului; n producie metodele serviciului fac apel la o baz de date pentru extragerea informaiilor: Serviciu Java
package tutorials.services.complextype; public class ContactService implements IContactService { public Contact[] getContacts() { Contact[] contacts = new Contact[2]; contacts[0] = new Contact(); contacts[0].FirstName = "Iulian"; contacts[0].LastName = "Ilie-Nemedi"; contacts[0].Email = "iulian.nemedi@ase.ro"; contacts[1] = new Contact(); contacts[1].FirstName = "Radu"; contacts[1].LastName = "Constantinescu"; contacts[1].Email = "radu.constantinescu@ase.ro"; return contacts; } }

clientul .NET apeleaz serviciul prin intermediul unui proxy care conine clase pentru deserializarea obiectelor complexe pe baza schemei XSD extras din descriptorul WSDL al serviciului: Client C#
using System; namespace Tutorials.ComplexTypeServices.ClientForNetService

Reele de calculatoare {

class Client { [STAThread] static void Main(string[] args) { ContactProxy.Contact[] contacts = new ContactProxy.ContactService().GetContacts(); for(int i = 0; i < contacts.Length; i++) Console.WriteLine("Contact:\n" + "\tFirst Name: " + contacts[i].FirstName + "\n" + "\tLast Name: " + contacts[i].LastName + "\n" + "\tEmail: " + contacts[i].Email + "\n"); } } }

clientul Java are nevoie de o clas pentru reprezentarea obiectului complex deserializat; pentru construirea acesteia platforma Glue Professional ofer utilitarul schema2java; apelarea din Java presupune importarea pachetelui cu definirea tipului de date complex i a interfeei serviciu; clasa proxy se obine prin apelul metodei statice electric.revistry.Registry.bind: Client Java
package tutorials.services.complextype; import electric.registry.Registry; import electric.registry.RegistryException; public class Client { public static void main(String[] args) throws RegistryException { IContactService proxy = (IContactService) Registry.bind(args[0], IContactService.class); Contact[] contacts = proxy.getContacts(); for(int i = 0; i < contacts.length; i++) System.out.println("Contact:\n" + "\tFirst Name: " + contacts[i].FirstName + "\n" + "\tLast Name: " + contacts[i].LastName + "\n" +

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE "\tEmail: " + contacts[i].Email + "\n");

existena unor tipuri de date elementare echivalente face posibil serializarea pe o platform i deserializarea pe alta a tipurilor de date complexe; chiar i n absena mecanismelor de serializare / deserializare ale containerelor de servicii Web, portabilitatea unui singur tip elementar, de exemplu String, permite traversarea platformei de ctre un obiect complex, n ultim instan programatorul putnd folosi serializarea i deserializarea String a obiectelor; 7.2.3 Apelarea serviciilor Web n mod asincron Utilitarul wsdl.exe genereaz metode pentru apelarea serviciului att n mod sincron, ct asincron. Pentru apelarea asincron se genereaz cte o pereche de metode Begin_Apel_Metod i End_Apel_Metod. Apelarea asincron a unui serviciu Web presupune iniierea apelului prin execuia metodei Begin care nregistreaz o funcie delegat callback ce va fi apelat la obinerea rspunsului de la metoda serviciului. presupunem un serviciu Web a crui execuie poate dura un timp mai ndelungat, avnd o metod care determin toate numerele prime pn la un numr dat ca parametru: Serviciu C#
using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Web; using System.Web.Services; namespace Tutorials.AsyncCallService.Service { [WebService(Namespace = "http://aris.ase.ro/schemas")] public class PrimeNumbersService : System.Web.Services.WebService { public PrimeNumbersService() { InitializeComponent(); }

Reele de calculatoare #region Component Designer generated code private IContainer components = null; private void InitializeComponent() { } protected override void Dispose( bool disposing ) { if(disposing && components != null) { components.Dispose(); } base.Dispose(disposing); } #endregion [WebMethod] public int[] GetPrimeNumbers(int n) { bool[] isNotPrime = new bool[n + 1]; for(int i = 2; i <= n - 1; i++) if(isNotPrime[i] == false) for(int j = 2; j * i <= n; j++) isNotPrime[j * i] = true; ArrayList numbers = new ArrayList(); for(int i = 2; i <= n; i++) if(isNotPrime[i] == false) numbers.Add(i); return (int[]) numbers.ToArray(typeof(int)); }

Interfaa serviciului Java


package tutorials.services.asynccall; public interface IPrimeNumbersService { public int[] getPrimeNumbers(int n); }

se implementeaz interfaa printr-o clas serviciu: Serviciu Java


package tutorials.services.asynccall; public class PrimeNumbersService implements IPrimeNumbersService { public int[] getPrimeNumbers(int n) { boolean[] isNotPrime = new boolean[n + 1]; int found = 0;

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE for(int i = 2; i <= n - 1; i++) if(isNotPrime[i] == false) { found++; for(int j = 2; j * i <= n; j++) isNotPrime[j * i] = true; } int[] numbers = new int[found]; int k = 0; for(int i = 2; i <= n; i++) if(isNotPrime[i] == false) numbers[k++] = i; return numbers;

se public serviciul pe un context Glue prin JMS (Java Messaging Services): Aplicaie gazd pentru serviciu Java
package tutorials.services.asynccall; import electric.registry.Registry; import electric.registry.RegistryException; import electric.registry.Registry; import electric.server.jms.JMS; public class AsyncServer { public static void main(String[] arguments) throws Exception { JMS.startup("jms:///AsyncCallService"); Registry.publish("PrimeNumbersService", new PrimeNumbersService()); } }

clientul .NET conine o metod care va fi apelat n mod asincron la primirea rezultatelor apelului ctre metoda serviciului Web: Metoda clientului C#
public void LoadComplete(IAsyncResult result) { this.Cursor = Cursors.Default; this.numbers = this.service.EndGetPrimeNumbers(result); string message = ""; for(int i = 0; i < this.numbers.Length; i++) message += (message.Length > 0 ? ", " : "") +

Reele de calculatoare this.numbers[i].ToString(); Console.WriteLine(message);

apelul sincron al metodei serviciului este nlocuit de apelul asincron al componentei Begin corespunztoare acesteia:
this.service.BeginGetPrimeNumbers(n, new AsyncCallback(this.LoadComplete), null);

spre deosebire de implementarea .NET n care clientul apela alt metod a clasei proxy, pe platforma Glue Professional clientul apeleaz serviciul asincron prin intermediul aceleai interfee ca i pentru apelurile sincrone, utiliznd ns alt URL pentru serviciu, ceea ce face ca cererile s fie tratate asincron prin JMS: Client Java
package tutorials.services.asynccall; import electric.registry.Registry; import electric.registry.RegistryException; public class Client { public static void main(String[] args) throws RegistryException { String url = "jms://AsyncCallService/PrimeNumbersService.wsdl"; IPrimeNumbersService proxy = (IPrimeNumbersService) Registry.bind(url, IPrimeNumbersService.class); int n = args.length == 1 ? Integer.parseInt(args[0]) : 1000; int[] numbers = proxy.getPrimeNumbers(n); String message = ""; for(int i = 0; i < numbers.length; i++) message += (message.length() > 0 ? ", " : "") + numbers[i]; System.out.println(message); } }

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

7.2.4 Publicarea unei componente ca serviciu Web Componentele COM+ sunt implicit apelabile la distan deoarece clasa MarshalByRefObject. Publicarea unei componente ServicedComponent ca serviciu pe IIS presupune urmtoarele aciuni: se scrie o component de serviciu, spre exemplu care necesit accesul tranzacional la o baz de date pentru a nregistra o comand; tipurile de date i interfaa de apel de la distan trebuie organizate ntr-o bibliotec referit att de serviciu care implementeaz interfaa, ct i de clientul care face apelul ctre serviciu: extind Obiectul tranzaciei
using System.Xml.Serialization; namespace Tutorials.ServicedComponents.Common { [XmlRoot("Order", Namespace = "http://aris.ase.ro/schemas")] public class Order { [XmlElement("Product", typeof(string))] public string Product; [XmlElement("Quantity", typeof(int))] public int Quentity; } }

Interfaa de apel
namespace Tutorials.ServicedComponents.Common { public interface IDeliveryService { bool PlaceOrder(Order order); } }

se implementeaz interfaa de aplel prin intermediul unei componente de serviciu gestionat tranzacional n COM+: Component de serviciu
using System; using System.EnterpriseServices;

Reele de calculatoare namespace Tutorials.ServicedComponents { [Transaction(TransactionOption.Required)] [JustInTimeActivation(true)] public class DeliveryService : ServicedComponent, Common.IDeliveryService { #region IDeliveryService Members [AutoComplete] public bool PlaceOrder(Common.Order order) { return DateTime.Now.Ticks % 2 == 0; } #endregion

se creeaz o aplicaie Web pe IIS n care se public binarele componentei SC mpreun cu un descripor Web.config de forma: Descriptor Web
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.runtime.remoting> <application> <service> <wellknown mode="SingleCall" type="Tutorials.ServicedComponents.DeliveryService,Tutorials.S ervicedComponents" objectUri="/ServicedComponents/DeliveryService.SOAP"/> </service> </application> </system.runtime.remoting> </configuration>

descriptorul Web configureaz n cadrul aplicaiei un serviciu monocanal SOAP, preciznd tipul clasei serviciu, assembly-ul n care se gsete i adresa la care va rspunde. clientul nregistreaz un canal HTTP, obine de la activator o referin local a obiectului la distan, prin intermediul creia va putea apela metodele interfeei serviciului:

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

Client pentru componeta de serviciu


using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Http; namespace Tutorials.ServicedComponents.Client { class Client { [STAThread] static void Main(string[] args) { try { HttpChannel channel = new HttpChannel(); ChannelServices.RegisterChannel(channel); Common.IDeliveryService service = (Common.IDeliveryService) Activator.GetObject(typeof(Common.IDeliveryService), "http://localhost/ServicedComponents/DeliveryService.SOAP"); Common.Order order = new Common.Order(); order.Product = "A Product"; order.Quentity = 1; Console.WriteLine(service.PlaceOrder(order) ? "Your order has been placed." : "Your order has been rejected."); } catch(Exception exception) { Console.WriteLine(exception.ToString()); } } }

Componentele EJB gzduite de containere EJB ale serverelor de aplicaii Java Enterprise sunt apelabile prin Remote Method Invocation (RMI). Pentru a putea apela o component EJB prin intermediul protocolului SOAP, platformele Java de servicii Web intercepteaz mesajele SOAP/HTTP ale clientului pe care le ruteaz conform descriptorilor de publicare ctre un anumit serviciu. Serviciile de tip EJB sunt gzduite de un

Reele de calculatoare

procesor care gestioneaz starea acestora, mapeaz metoda apelului SOAP peste metoda corespunztoare a interfeei Remote a componentei EJB pe care o apeleaz apoi prin RMI. De fapt, un serviciu Web de tip EJB nu este altceva dect un client Java pentru componenta EJB gzduit pe serverul de aplicaii care este apelat prin SOAP de ctre clienii externi. pentru o component EJB publicat n JBoss pe contextul ejb/Exchange se creeaz o aplicaie Glue folosind utilitarul NewApp care de fapt copiaz coninutul directorului apptemplate ntr-un director cu numele aplicaiei; descriptorul aplicaiei Web, fiierul web.xml, aflat n subdirectorul de resurse WEB-INF, conine maparea servlet-urilor contextului curent, printre care se remarc servlet-ul glue-SOAP, responsabil cu rutarea mesajelor SOAP ctre procesorul serviciului:
<servlet> <servlet-name>glue-SOAP</servlet-name> <servletclass>electric.server.http.ServletServer</servlet-class> <init-param> <param-name>httpRegistryRoot</param-name> <param-value>/</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> de asemenea, tot n fiierul web.xml este configurat i procesorul de servicii SOAPServletContext; n subdirectorul services din WEB-INF se creeaz cte un

descriptor XML pentru fiecare serviciu gzduit de aplicaia curent, avnd drept model fiierul sample.xml; n cazul publicrii unei componente EJB ca serviciu Web fiierul descriptor are urmtoare form:
<?xml version='1.0'?> <service> <constructor> <class>electric.service.ejb.StatelessSessionBeanService</class > <args> <contextFactory>org.jnp.interfaces.NamingContextFactory</conte xtFactory>

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE <url>jnp://localhost:1099</url> <jndiName>ejb/Exchange</jndiName> <homeInterfaceName>tutorials.ejbstaff.interfaces.ExchangeHome< /homeInterfaceName> </args> </constructor> </service>

descriptorul precizeaz tipul de Bean publicat, contextul JNDI al componentei EJB pe serverul de aplicaii i interfaa Remote de apel; se construiete arhiva aplicaiei Web folosind utilitarul jar cu comanda:
jar cf nume_arhiv.war director_aplicaie

se public prin copiere n zona de publicare a serverului JBoss (server/default/deploy) pentru apelarea dintr-un client Java este necesar includerea unei interfee similare interfeei Remote a componentei EJB: Interfa de apel la client
package tutorials.ejbstaff.client; public interface IExchange { public double getRate(String county1, String country2); }

Client Java
package tutorials.ejbstaff.client; import electric.registry.Registry; public class Client { public static void main(String[] args) { try { String url = args[0]; IExchange exchange = (IExchange) Registry.bind(url, IExchange.class); double rate = exchange.getRate("usa", "japan"); System.out.println("usa/japan exchange rate = " + rate); } catch(Exception exception) { System.out.println(exception.toString()); } } }

Reele de calculatoare

pentru apelarea dintr-un client .NET se construiete o clas proxy prin adugarea unei referine Web la descriptorul WSDL al serviciului; platforma Glue gzduit n JBoss livreaz descriptorul WSDL la adresa:
http://server:port/nume_aplicaie/services/nume_serviciu.wsdl unde numele aplicaiei este numele fiierului arhiv .war copiat n spaiul de publicare JBoss, iar numele serviciului este numele fiierului descriptor .xml din subdirectorul services al directorului de resurse al aplicaiei Web (WEBINF):

Client J#
package Tutorials.EjbStaff.NETClient; public class Client { /** @attribute System.STAThread() */ public static void main(String[] args) { EjbService.Exchange exchange = new EjbService.Exchange(); double rate = exchange.getRate("usa", "japan"); System.out.println("usa/japan exchange rate = " + rate); } }

7.2.5 Servicii Web virtuale Platforma Glue Professional permite publicarea unui descriptor WSDL pentru un serviciu Web virtual avnd la baz un set de interfee. Un serviciu virtual nu are ataat o implementare static n sensul consacrat; n schimb se ofer posibilitatea nregistrrii unor clase care s trateze la momentul apelului mesajele SOAP destinate serviciului virtual. Aceast legare poate fi de nivel nalt sau de nivel sczut. Legarea de nivel nalt utilizeaz clasa VirtualService, n timp ce legarea de nivel sczut face apel la clasa VirtualSOAPHandler. Implementarea unui serviciu virtual High-Level se declar metodele interfeei serviciu:
package tutorials.services.virtual; public interface IExchange { public double getRate(String country1, String country2); }

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

se ofer cel puin o implementare pentru interfaa serviciu. Arhitectura serviciilor virtuale High-Level are la baz mecanismul fabricii de obiecte. Spre deosebire de serviciile Web obinuite care ataeaz o singur implementare unei interfee publicate, serviciile virtuale permit ataarea la momentul apelului a implementrii adecvate contextului cererii:
package tutorials.services.virtual; public class Exchange implements IExchange { public double getRate(String country1, String country2) { return Math.random() * 100; } }

tratarea legrii implementrii concrete de interfaa publicat nu mai are loc n descriptorul aplicaiei sau n aplicaia gazd, ci ntr-o clas special care se folosete de mecanismul de Reflection pentru dispecerizarea apelurilor metodelor serviciului ctre obiectul ce-l deservete la momentul apelului:
package tutorials.services.virtual; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class ExchangeHandler implements InvocationHandler { private IExchange exchange = new Exchange(); public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return method.invoke(exchange, args); } }

publicarea se poate face printr-un descriptor care precizeaz interfaa i handler-ul virtual sau printr-o aplicaie consol gazd ce poate fi instalat s ruleze ca serviciu de platform, n primul caz fiind nevoie de un server de aplicaii gazd, gen Glue sau un container pentru Glue (de exemplu Tomcat, JBoss, WebSphere, WebLogic etc.):
package tutorials.services.virtual; import electric.util.Context; import electric.registry.Registry; import electric.server.http.HTTP; import electric.service.virtual.VirtualService; public class ExchangePublish {

Reele de calculatoare public static void main(String[] args) throws Exception

HTTP.startup("http://localhost:8004/glue"); ExchangeHandler handler = new ExchangeHandler(); VirtualService exchange = new VirtualService(IExchange.class, handler); Context context = new Context(); Registry.publish("exchange", exchange, context); } }

clientul apeleaz serviciul virtual similar apelrii unuia obinuit, prin legarea la URL-ul descriptorului WSDL i conversia obiectului proxy obinut la tipul interfeei serviciului, prin care ulterior poate apela metodele acestuia, indiferent de clasa care le implementeaz pe server:
package tutorials.services.virtual; import electric.registry.Registry; public class ExchangeInvoke { public static void main(String[] args) throws Exception { String url = "http://localhost:8004/glue/exchange.wsdl"; IExchange exchange = (IExchange) Registry.bind(url, IExchange.class); double rate = exchange.getRate("usa", "japan"); System.out.println("usa/japan exchange rate = " + rate); } }

Implementarea unui serviciu virtual Low-Level un serviciu virtual de tip Low-Level implementeaz interfaa electric.SOAP.ISOAPHandler pentru a rspunde apelurilor SOAP ctre metodele serviciului:
package tutorials.services.virtual; import java.rmi.RemoteException; import electric.xml.Element; import electric.xml.XPath; import electric.xml.io.encoded.EncodedWriter; import electric.SOAP.ISOAPHandler; import electric.SOAP.SOAPMessage;

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE import electric.util.Context; public class ExchangeSOAPHandler implements ISOAPHandler { static final XPath country1 = new XPath("getRate/[1]"); static final XPath country2 = new XPath("getRate/[2]"); public SOAPMessage handle(SOAPMessage request, Context context) throws RemoteException, SecurityException { String arg0 = request.getBody().getElement(country1).getString(); String arg1 = request.getBody().getElement(country2).getString(); SOAPMessage response = new SOAPMessage(); Element body = response.addBody(); Element result = body.addElement(); result.setNamespace("n", "http://tempuri.org/examples.publish.IExchange"); result.setName("n", "getRateResponse"); EncodedWriter writer = new EncodedWriter(result); writer.writeDouble("Result", Math.random() * 100); return response; } }

publicarea presupune nregistrarea interfeei i a handler-elor aferente, dintre care esenial este cel care trateaz mesajele SOAP i prin intermediul cruia rspunde de fapt serviciul:
package tutorials.services.virtual; import electric.registry.Registry; import electric.server.http.HTTP; import electric.SOAP.virtual.VirtualSOAPHandler; public class ExchangeSOAPPublish { public static void main(String[] args) throws Exception { HTTP.startup("http://localhost:8004/glue"); ExchangeHandler invokeHandler = new ExchangeHandler(); ExchangeSOAPHandler SOAPHandler = new ExchangeSOAPHandler(); VirtualSOAPHandler exchange = new VirtualSOAPHandler(IExchange.class, invokeHandler, SOAPHandler); Registry.publish("exchange", exchange); } }

Reele de calculatoare

apelarea unui serviciu virtual de tip Low-Level este similar apelrii oricrui serviciu Web XML, clientul netiind detaliile implementrii serviciului. 7.2.6 Autentificarea i autorizarea accesului Autentificarea presupune identificarea utilizatorului, adic verificarea identitii utilizatorului. Pe platforma .NET avem trei categorii de autentificri, categorii dictate de cine anume implementeaz mecanismul de autentificare: IIS: Internet Information Server ofer urmtoare tipuri de autentificri: Anonymous Basic Authentification Digest Authentification Integrated Windows Authentification (NTLM sau Kerberos) ASP.NET: introduce dou noi mecanisme de autentificare: Forms: dezvoltatorul creeaz o pagin prin care se cere utilizatorului s-i introduc informaiile de identificare. Pe baza unei proceduri de validare a datelor introduse se decide dac este autentificat sau nu. Passport: autentificarea utilizatorului se face cu ajutorul unei tere pri; pentru detalii despre cum anume se poate implementa acest mecanism de autentificare vezi www.passport.com Antete SOAP personalizate: prin care dezvoltatorul i implementeaz propriul mecanism de autentificare. De exemplu, informaiile despre utilizator s fie trimise sub form de parametri la metoda serviciului web. Etapele autentificrii de tip antete SOAP personalizate: crearea unui antet SOAP prin derivarea unei clase din SOAPHeader coninnd credeniale criptate; crearea i nregistrarea n web.config a unui modul HTTP care s implementeze metoda Init i evenimentele Register ale interfeei IHttpModule; tratarea evenimentului Custom Authentification n Global.asax verificndu-se dac informaiile de autorizarea accesului nu exist deja n colecia Context.Cache. Dac nu sunt stocate n cache-ul aplicaiei informaii despre rolul utilizatorului, atunci se construiete un obiect de tip Credentials care se ataeaz unui obiect de tipul Principal declarat ca utilizator curent al aplicaiei prin ncrcarea

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

obiectului Context.User. Verificarea contextului de rulare al aplicaiei se face prin intermediul atributelor PrincipalPermission asociat metodelor expuse de serviciu Web. Autorizarea presupune acordarea drepturilor corespunztoare rolului jucat de fiecare utilizator autentificat n sistem i vizeaz mai multe niveluri de acces la resurse, i anume: URL Authorization; File Authorization; Principal Permissions; .NET Roles. Platforma GLUE ofer suport pentru autentificarea pe client i pe server, fiind compatibil cu majoritatea platformelor care utilizeaz autentificarea de tip HTTP. n varianta Professional, GLUE include suport pentru integrarea cu standardul JAAS, care permite autentificarea bazat pe sistemul drepturilor de acces LDAP/NT. Noiunile fundamentale pentru implementarea autentificrii pe platfoma GLUE sunt: Principal, entitate asociat identitii utilizatorului; Role, colecie de drepturi de acces asociat unei identiti a utilizatorilor autentificai; Permission, o aciune care poate fi executat de un utilizator avnd un asociat un anumit rol; Realm, o colecie de informaii viznd identitatea utilizatorului, rolul asociat i permisiunile aferente; Guard, o constrngere care utilizeaz o colecie Realm pentru a verifica c un utilizator autentificat are dreptul s execute o anumit aciune. Pentru impementarea autentificrii i autorizrii accesului pe platforma GLUE se au n vedere urmtoarele etape: se editeaz fiierul WEB-INF/glue-config.xml n care se precizeaz tipul autentificrii, n acest caz JAAS:
<realm> <constructor> o <class>electric.security.jaas.JAASRealm</class> o <args> <name>jaas</name> <config>security\jaas.config</config> o </args> </constructor> </realm> se editeaz fiierul WEB-INF/services/serviciu.xml n care se

specific publicarea autentificrii i rolul aferent:

Reele de calculatoare <publish>yes</publish> <role>denumire_rol</role> se editeaz fiierul WEB-INF/web.xml n care se precizeaz

metoda de autentificare:
<login-config> <realm-name>acl</realm-name> </login-config>

Pentru autentificarea apelului la un serviciu Web securizat se utilizeaz obiectul ProxyContext care va fi ncrcat cu datele de autentificare ale utilizatorului declarat n fiierele de configurare, astfel:
ProxyContext context = new ProxyContext(); context.setAuthUser("denumire_rol"); context.setAuthPassword("parol"); ISample sample = (ISample) Registry.bind(ur_wsdll, ISample.class, context);

7.3 Nivelul de prezentare 7.3.1 Construirea unei imagini ntr-o pagin ASP.NET Pentru exemplificare vom considera o pagin ASP.NET care afieaz ca rezultat o imagine extras dintr-o baz de date i primit de la un serviciu Web sub forma unui vector de octei. Pagin ASP.NET cu Code Behind
<%@ Page language="c#" Codebehind="RoomImage.aspx.cs" AutoEventWireup="false" Inherits="Tutorials.AspNet.RoomImage" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > <html> <head> <title>RoomImage</title> <meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1"> <meta name="CODE_LANGUAGE" Content="C#"> <meta name=vs_defaultClientScript content="JavaScript"> <meta name=vs_targetSchema content="http://schemas.microsoft.com/intellisense/ie5"> </head> <body MS_POSITIONING="GridLayout"> <form id="ImageForm" method="post" runat="server"> </form> </body> </html>

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

o pagin Web extinde clasa System.Web.UI.Page; are acces la interfaa server-ului Web prin obiecte de forma Request, Response, Session, Context etc; punctul de intrare n program este handler-ului evenimentului Load al paginii, n acest caz funcia Page_Load; Clasa paginii
using System; using System.Collections; using System.ComponentModel; 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 System.IO; namespace Tutorials.AspNet { public class RoomImage : System.Web.UI.Page { private void Page_Load(object sender, System.EventArgs e) { if(this.Request["RoomId"]!=null) { this.Response.ContentType = "image/jpg"; Administrator.AdministratorService service = new Administrator.AdministratorService(); byte[] image = service.GetRoomImageData(int.Parse(this.Request["RoomId"])); if(image == null) { FileStream fileStream = new FileStream(this.Server.MapPath("tmp/admin_no_image.jpg"), FileMode.Open); image = new byte[new FileInfo(this.Server.MapPath("tmp/admin_no_image.jpg")).Length ]; fileStream.Read(image, 0, image.Length); fileStream.Close(); }

Reele de calculatoare

this.Response.OutputStream.Write(image, 0, image.Length); } } #region Web Form Designer generated code override protected void OnInit(EventArgs e) { InitializeComponent(); base.OnInit(e); } private void InitializeComponent() { this.Load += new System.EventHandler(this.Page_Load); } #endregion } }

7.3.2 Apelarea unei componente dintr-un servlet Tehnologia Servlet a fost gndit s suplineasc deficienele arhitectuiri CGI legate de gestiunea proceselor i firelor de execuie, precum i de securitate, avnd urmtoarele caracteristici funcionale: pune la dispoziia programatorului obiecte pentru accesarea interfeei server-ului Web cum ar fi HttpServletRequest, HttpServletResponse etc., organizate n pachetele javax.servlet i javax.servlet.http; rspunsul generat la ieire devine sursa paginii HTML, astfel nct un servlet nu separ procesrile i coninutul rezultat de formatarea acestuia; binarele unui servlet trebuie publicate n subdirectorul WEBINF/classes al unui context Web; pentru a putea fi accesat, servletului i se asociaz un URL prin intermediul fiierului de configurare web.xml sau cu ajutorul meta-tag-ului comentariu @web.servlet-mapping, dac se utilizeaz un mediu de construire a arhivei .war cum ar fi Ant sau XDoclet. Servlet care acceseaz o component EJB
package tutorial.web; import java.io.IOException; import java.io.PrintWriter;

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE import javax.naming.Context; import javax.naming.InitialContext; import javax.rmi.PortableRemoteObject; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import tutorial.interfaces.Fibo; import tutorial.interfaces.FiboHome; /** * @web.servlet name="Compute" display-name="Computation Servlet" * description="Servlet that compute Fibonacci suite" * * @web.servlet-mapping url-pattern="/Compute" * * @web.ejb-ref name="ejb/Fibo" type="Session" * home="tutorial.interfaces.FiboHome" * remote="tutorial.interfaces.Fibo" description="Reference to the * Fibo EJB" * * @jboss.ejb-ref-jndi ref-name="ejb/Fibo" jndiname="ejb/Fibo" */ public class ComputeServlet extends HttpServlet { private FiboHome home; public ComputeServlet() { super(); } public void init(ServletConfig config) throws ServletException { try { Context context = new InitialContext(); Object ref = context.lookup("java:/comp/env/ejb/Fibo"); home = (FiboHome) PortableRemoteObject.narrow(ref, FiboHome.class); } catch (Exception e) { throw new ServletException("Lookup of java:/comp/env/ failed"); } } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

Reele de calculatoare response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html><head><title>"); out.println("Fibonaci Computation"); out.println("</title></head>"); out.println("<body>"); out.println("<h1>"); out.println("Fibonaci Computation"); out.println("</h1>"); try { Fibo bean = home.create(); int limit = 0; String value = request.getParameter("limit"); if (value != null) { try { limit = Integer.parseInt(value); } catch (Exception e) { } } double[] result = bean.compute(limit); bean.remove(); out.println("<p>"); out.print("The "); out.print(limit); out.print(" first Fibonacci numbers "); for (int i = 0; i < result.length; i++) { out.println("<br>"); out.println(i); out.println(" : "); out.println(result[i]); } out.println("</p>"); } catch(Exception e) { out.println(e.getMessage()); e.printStackTrace(out); } finally { out.println("</body></html>"); out.close(); } } }

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

7.3.3 Definirea unui Custom Tag n JSP Tehnologia JSP a fost dezvoltat din Servlet cu scopul separrii prelucrrilor i coninutului de formatarea rezultatelor, avnd urmtoarele caracteristici: permite intercalarea de cod procesat pe server cu formatri HTML; la momentul primei cereri o pagin JSP este translatat ntr-un servlet de ctre serverul de aplicaii, care este apoi compilat n cod de octei i interpretat; pentru orice alt cerere se compar data salvrii paginii JSP cu data fiierului binar generat, verificndu-se astfel dac este necesar translatarea i recompilarea paginii; Principiul separrii procesrilor de formatarea rezultatelor este cel mai bine exemplificat n standarduol JSP 2.0 de tehnologia Custom Tags. Pentru implementarea unui Custom Tag este necesar scrierea urmtoarelor componente: o clas tag handler: reprezint o clas Java care precizeaz modul n care tag-ul produce rezultat HTML; extinde clasa TagSupport sau BodyTagSupport i implementeaz interfaa Tag din pachetul javax.servlet.jsp.tagext, fiind publicat n contextul aplicaiei mpreun cu celelalte clase i componente Bean ale acesteia; un fiier descriptor al bibliotecii de tag-uri: descrie n format XML numele, atributele i handler-ele tag-urilor, fiind publicat mpreun cu paginile JSP sau pe o anumit adres URL dac aparine altui context dect cel al aplicaiei Web curente; fiierele JSP: import biblioteca de tag-uri prin intermediul fiierului descriptor, asociindu-i un prefix de tag pentru a putea utiliza tag-uri ale acesteia n codul JSP. Implementarea handler-ului tag-ului necesit: extinderea clasei TagSupport din pachetul javax.servlet.jsp.tagext; rescrierea metodei doStartTag; se obine o referin la obiectul JspWriter al fluxului de procesare a ieirii paginii, prin care se genereaz coninutul JSP; metoda ntoarce constanta SKIP_BODY n cazul unui tag simplu, fr imbricri de tip corp de tag;

Reele de calculatoare

codul este translatat n servlet la momentul primei cereri a paginii JSP, genernd apoi rspuns HTML prin execuie pentru fiecare cerere n parte. Custom Tag fr imbricare
package tutorials.customtags; import java.io.IOException; import java.util.Random; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.tagext.TagSupport; public class SimpleRandomTag extends TagSupport { protected int length; public int doStartTag() { try { JspWriter out = this.pageContext.getOut(); out.print(new Random().nextInt(this.length)); } catch(IOException exception) { System.out.println(exception.toString()); } return SKIP_BODY; } }

Se scrie apoi descriptorul XML al bibliotecii de tag-uri custom, atfel: dup antetul documentului XML cu DOCTYPE, tag-ul rdcin este taglib; fiecare tag este definit de elementul tag preciznd numele tag-ului i handler-ul care-l trateaz; nodul name precizeaz numele tag-ului curent; nodul tagclass indic numele clasei handler calificat cu pachetul acesteia; nodul info conine o scurt descriere a tag-ului curent. Descriptor pentru biblioteca de tag-uri custom
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd"> <taglib> <tag> <name>simpleRandom</name>

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE <tagclass>tutorials.customtags.SimpleRandomTag</tagclass> <info>Outputs a random integer.</info> </tag> </taglib>

Utilizarea unui tag dintr-o bibliotec ntr-o pagin JSP presupune: se import biblioteca de tag-uri preciznd locaia acesteia relativ la contextul aplicaiei sau absolut:
<%@ taglib uri="customjsp-taglib.tld" prefix="customjsp" %>

se definete un prefix asociat bibliotecii importate


<prefix:tagName />

se apeleaz tag-uri din bibliotec prefixate corespunztor importului


<customjsp:simpleRandom />

Pagin JSP care utilizeaz un Custom Tag


<%@page contentType="text/html" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <%@ taglib uri="/WEB-INF/jsp/CustomTags.tld" prefix="mytags" %> <html> <head> <title>JSP Page</title> </head> <body bgcolor="#FFFFFF"> <mytags:simpleRandom/> </body> </html>

Acest mecanism poate fi extins sub forma unui custom tag cu atribute i coninut: se declar n handler-ul tag-ului proprieti pentru fiecare atribut al su; setarea unui atribut n cadrul tag-ului echivaleaz cu apelarea metodei setter a obiectului handler; se declar atributul n descriptorul tag-ului

Reele de calculatoare

Custom Tag care extinde un tag simplu prin adugarea unui atribut
package tutorials.customtags; public class RandomTag extends SimpleRandomTag { public void setLength(int length) { this.length = length; } }

Descriptor pentru un Custom Tag cu atribute


<name>random</name> <tagclass>tutorials.customtags.RandomTag</tagclass> <attribute> <name>length</name> <required>false</required> <rtexprvalue>false</rtexprvalue> </attribute> </tag> <tag>

7.4 Interoperabilitatea platformelor .NET i J2EE Pe piaa platformelor software pentru aplicaii distribuite de ntreprindere exist n prezent dou mari clase consacrate: .NET i J2EE. Dei din raiuni de compatibilitate i integrare am fi tentai s optm pentru o soluie omogen cnd se pune problema implementrii unui sistem de ntreprindere, totui teoria avantajului comparativ evideniaz necesitatea cooperrii celor mai bune tehnologii pentru fiecare nivel n parte. Interoperabilitatea celor dou platforme este necesar din urmtoarele considerente practice: eterogenitatea platformelor hardware i software existente n mediul de ntreprindere; controlul total este ineficient; specificul aplicaiei trebuie avut n vedere la alegerea unei platforme; ca rezultat, majoritatea companiilor combin tehnologiile; la baza oricrui demers de combinare a platformelor software st satisfacerea nevoilor clienilor ntr-o msur ct mai mare.

Implementarea aplicaiilor distribuite pe platformele .NET i J2EE

Potrivit percepiei comune o aplicaie eterogen care se bazeaz pe cele dou platforme ar trebui s foloseasc tehnologii .NET pe partea de client i tehnologii J2EE pe partea de server, deoarece: soluia .NET este mai bun pe partea de client, avnd drept atuuri: interfaa client adaptat i optimizat pentru platforma Windows, folosit de majoritatea utilizatorilor finali ai produselor informatice de ntreprindere; ofer suport extins pentru dispozitive mobile i protocoale dedicate gen WAP; se integreaz cu suita de aplicaii Office. pe de alt parte, soluia J2EE este potrivit pentru nivelurile logicii aplicaiei i de acces la date, deoarece: ofer o performan ridicat, avnd un grad nalt de scalabilitate; este mai mentenabil dect .NET; are un raport calitate / pre superior, oferind soluii fiabile. Aceast abordare nu este ntotdeauna cea optim. Se pot imagina trei scenarii de interoperabilitate plauzibile: punte RMI .NET Remoting: are drept principal avantaj performana ridicat a comunicaiei, putnd utiliza o gam foarte larg de protocoale peste TCP, unele chiar mpachetate binar i arhivate; ca dezavantaje se remarc complexitatea ridicat, necesitnd convertoare de protocol, proprietatea obiectelor COM+ pe platforma .NET precum i dependena de versiunea obiectelor. apelul prin cozi de mesaje: prezint avantajul cuplrii slabe, permind comunicarea intra i inter-niveluri, n condiiile existenei unui suport extins pentru tranzacii i securitate; ca principal dezavantaj se nscrie absena operaiilor sincrone, introducndu-se penalizri temporare semnificative pentru sincronizare, alturi de posibile neajunsuri de acces pe porturi prin Firewall.

Reele de calculatoare

servicii Web: se remarc drept varianta optim de interoperabilitate pentru cele mai multe situaii datorit cuplrii slabe, n condiiile executrii de operaii sincrone, ct i asincrone, fr restricii de tip Firewall sau conversii de protocoale de comunicaie; pn n prezent are totui dezavantajul implementrii pariale a standardelor pentru securitate, dar mai ales pentru tranzacii. Dezvoltarea modelului GXA (Global XML Web Services Architecture) intenioneaz s nlture aceste neajunsuri prin protocoale de tipul WS-Security, WS-Attachement sau WS-Routing.

You might also like