Les Applets .NET 


Introduction

Java a gagné ses lettres de noblesse au début, il faut bien l'avouer, grâce en partie aux applets. Plus précisément, ce fût Sun qui, à l'époque, profita de l'absence totale de mécanisme permettant de dynamiser les pages Web pour vendre ce concept novateur de téléchargement de code. Depuis, l'eau a coulé sous les ponts et les applets ont perdu petit à petit de leur splendeur. Cet état de fait s'explique par de multiples raisons. Tout d'abord, l'infrastructure des réseaux internet s'avère inadapté dans le cadre de téléchargement de code, des délais trop longs et des résultats trop souvent décevants par rapport aux bénéfices escomptés. Ajoutez à cela, les incompatibilités entre les navigateurs liées à la guerre que se livre Sun et Microsoft et vous comprendrez pourquoi les Applets n'ont jamais vraiment pu se démocratiser. 

Pour autant, il ne faut pas croire que la fin des applets est pour bientôt. Ainsi, il existe plusieurs facteurs qui tendent à démontrer le contraire. Ainsi, non seulement le débit des réseaux s'améliore de jours en jours, mais le marché des navigateurs continue de s'assainir au profit d'Internet Explorer rendant la compatibilité comme un critère moins décisif pour le développement d'applications Web. Quant à la gestion du téléchargement de code, elle devient plus "intelligente" et propose des mécanismes évolués de mise en cache. Enfin, la dernière raison, plus fondamentale, est qu'il existera toujours des cas où seules les applets seront réellement à même de répondre au besoin.  

Alors, il est vrai qu'en pleine période de promotion de WebServices et de client léger, parler des applets peut paraître quelque peu "démodé" mais ces deux technologies, loin de s'affronter, présentent un certain nombre de complémentarités intéressantes à étudier.  

Ce sera le sujet de cet article : vous faire découvrir, à travers une étude de cas détaillée, les avantages des applets .NET associées aux WebServices coté serveur. Mais aussi, décrire succinctement les similitudes et différences avec Java.

 

Que sont les applets Java et .NET ?

Les applets sont des petits programmes écrit en Java ou .NET s'insérant dans une page HTML et téléchargés sur le poste client. Le navigateur joue le rôle de conteneur en vérifiant que l'applet n'accède pas à une ressource locale ou n'essaye pas de communiquer avec un service situé dans le réseau interne (SGBD, ...). Les applets sont chargées par un Runtime, en l'occurrence une JVM pour Java et une CLR pour .NET, installé en tant que plug-in dans le navigateur. Il est nécessaire de disposer de part et d'autre des mêmes versions de classes Systèmes (JDK et BCL). A l'heure actuelle, seul Sun propose la compatibilité parfaite des applets en proposant le Java Plug-In et l'API Java Web Start permettant de faire fonctionner une applet sous Netscape et Internet Explorer alors que seul IE 5 (et plus) est en mesure de lire une applet .NET. Gageons qu'à l'avenir Microsoft proposera l'installation d'un plug-in sous Netscape afin de démocratiser l'utilisation des applets .NET. 

 

Etude de cas

L'étude de cas suivante racontée sous la forme d'une histoire fictive va nous permettre de découvrir l'ensemble des étapes nécessaire au développement d'une applet .NET. 

La société DNGFinances (DNG pour DotNetGuru ;-)) veut mettre à disposition de ses clients un graphique sous la forme de courbe lui permettant de suivre en temps réel l'évolution d'une valeur donnée. Non seulement la courbe doit être mise à jour tous les quarts d'heure, mais l'utilisateur doit avoir la possibilité de sélectionner un point de la courbe pour en extraire certaines données spécifiques (volume, variation horaire, ...). Les Gurus de l'équipe de développement ont d'abord pensé logiquement à écrire un client léger sous la forme d'une page HTML. Après discussions, il s'avère que la mise en place d'une telle solution requiert la construction dynamique d'une image au format gif ou jpg. Toutefois, cette option n'est pas satisfaisante dans la mesure où l'interactivité ne sera pas au rendez-vous. En effet, pour pouvoir sélectionner les points de la courbe, il faut construire une image dynamiquement, qui plus est "clickable". 

Ce cas d'école représente parfaitement une situation donnée où seule une applet est en mesure de répondre au besoin. Les Gurus ont pourtant pensé à écrire du DHTML associé à du Javascript mélangé à des contrôles complexes, mais le choix s'avérait binaire : implémenter une "usine à gaz" composée d'une myriade de technologies différentes avec un source inmaintenable ou développer simplement une applet. Les Gurus ont choisi avec sagesse l'applet. 

 

Avant de commencer les développements, le chef de projet Guru prend contact avec la société BoursoWeb, le fournisseur des données financières. Lors de l'entrevue, il s'avère que BoursoWeb dispose de WebServices permettant de récupérer en temps réel et à la demande le cours d'une valeur donnée. Les Gurus sont ravis, il leur suffit simplement d'écrire une applet qui appellera tous les quarts d'heure le service getStockRate() de la société BoursoWeb. Le programme source suivant nous montre le contenu de ce WebService : 

Imports System.Web.Services
Public
Class Service1
    Inherits System.Web.Services.WebService
    ' Methode GetStockRate renvoyant aléatoirement
    ' une valeur entière entre 0 et 100

    <WebMethod()> 
   
Public Function GetStockRate() As Integer
        Dim
r As Random
        r =
New Random()
        GetStockRate = r.Next(0, 100)
   
End Function
End
Class

WebService1.vb

 

Note : Attention, dans cet exemple, le serveur contenant le WebService doit être le même que celui qui héberge l'applet, cela pour des raisons de sécurité. Dans la pratique, les WebServices sont en règle générale situés sur un serveur tier. Dans ce cas, il vous faudra signer l'applet (enregistrement dans le GAC client manuellement).

Après avoir pris connaissance de l'interface du composant de BoursoWeb, nos Gurus se décident à coder une applet simple. Une rapide lecture sur la documentation des WinForms leur indique qu'une Applet .NET s'appelle aussi "Contrôle Client WinForms" et qu'elle doit être implémentée exactement comme un contrôle WinForms classique. C'est une première différence fondamentale avec Java. Alors qu'en Java, les applets héritent de java.applet.Applet, en .NET, les contrôles héritent de System.Windows.Forms.Control. Cette approche permet de tester une applet .NET comme n'importe quel contrôle situé dans un formulaire. Aussi, en Java, un outil spécial appelé appletviewer est nécessaire pour faire fonctionner l'applet en dehors du navigateur. 

Le code suivant nous illustre notre première applet .NET :

 

namespace DotNetGuru.Samples.Applet.ChartControl {
    using System;
    using System.ComponentModel;
    using System.Windows.Forms;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Reflection;

    public class ChartControl : System.Windows.Forms.Control {

        //*** Constructor

        public ChartControl() :base() {
            //Make sure the control repaints as it is resized
            SetStyle(ControlStyles.ResizeRedraw, true);      
            //Make back and foreground colors
	    base.BackColor = Color.White ;
            base.ForeColor = Color.Green ;
       }

        //Handle the paint event
        protected override void OnPaint(PaintEventArgs e) { 
	    Graphics g = e.Graphics;
	    //Continue all work  here ! 
	}
	   
    }
}

  
ChartControl.cs

Comparons ce code à une applet Java.

import java.awt.Graphics;

public class GraphApplet extends java.applet.Applet {
	int appWidth, appHeight ;
	public void init() {
 	appWidth = getSize().width;
	appHeight = getSize().height;
        
    }

    public void paint(Graphics g) {
        for (int x = 0 ; x < getSize().width ; x++) {
	    g.drawLine(x, (int)f(x), x + 1, (int)f(x + 1));
        }
    }

}

GraphApplet.java

Il existe des similitudes évidentes entre l'applet .NET et Java. Chacune utilise un canevas pour s'exécuter et les deux se basent sur des méthodes dites de "callback". Une méthode de callback est une méthode qui sera exécutée par le conteneur, en l'occurrence le navigateur, lorsqu'un évènement bien spécifique surviendra. Ainsi, la méthode Paint(Graphics) sera appelée par le navigateur pour redessiner l'applet à l'écran après que l'utilisateur ait réduit la fenêtre ou scrollé l'écran. 

Revenons à notre étude de cas initiale. Nos fameux Gurus n'en ont pas fini avec l'application, le source de l'applet doit être compilé puis testé. L'Assembly générée doit se trouver dans un répertoire connu du serveur Web pour être téléchargée sur le poste client. Ils choisissent donc de tout mettre dans le même répertoire par souci de simplicité. Ensuite, ils effectuent une simple compilation. 

L'étape suivante consiste à implémenter la page HTML qui servira de lanceur à l'applet. A l'heure actuelle, Visual Studio ne propose pas de mécanisme permettant de générer automatiquement cette page suivant un contrôle donné mais son contenu étant relativement simple cette opération pourra être réalisée à la main.

<head>
	<title>Applet SimpleChart DotNetGuru </title>
</head>
<html>
	<body>
	<!-- Put this tag in order to reference the applet and download it -->
	<object id="chartControl" 
		classid="ChartControl.dll#DotNetGuru.Samples.Applet.ChartControl.ChartControl" 
		height="250" 
		width="300" VIEWASTEXT>
	</object>
	<br>
	</body>
</html>

applet.html

 

Tout se trouve dans le Tag <object>, vous devez référencer l'Assembly à l'aide de l'attribut classid en spécifiant le nom de la DLL et la classe principale devant être chargée. Une fois cette opération réalisée, notre applet .NET est prête à être testée. Il suffit de lancer un navigateur et de taper l'URL suivante : http://localhost/SimpleApplet/applet.html 

Nos Gurus, après avoir testé l'API GDI+ par l'intermédiaire du namespace System.Drawing.Drawing2D, doivent s'atteler à réaliser l'invocation du Service Web. Un rapide coup d'oeil sur la documentation leur indique qu'il est  nécessaire au préalable de générer un Proxy coté client à l'aide de l'interface WSDL du service Web. Il existe plusieurs manières de faire : à l'aide de Visual Studio avec la création d'un Web Reference ou directement en ligne de commande avec l'outil wsdl.exe suivi de l'URL pointant vers l'interface (ex : http://www.boursoWeb.com/B2B/stocks.asmx?WSDL) ou http://localhost/GuruService/Service1.asmx?WSDL dans notre exemple local.

 

 

Ils disposent maintenant de plusieurs choix pour packager l'application finale : Intégrer le Proxy compilé à l'intérieur d'une seule Assembly contenant l'applet ou référencer dynamiquement le Proxy dans une autre DLL. Nos Gurus choisissent la première solution car elle simplifie la procédure de déploiement. Dans la pratique, il est préférable de garder le proxy à l'extérieur de votre Assembly car cela évite de recompiler entièrement l'application lorsque l'interface du WebService est modifiée.

Avant de réaliser la compilation finale, l'équipe de développement doit enrichir la méthode OnPaint() pour qu'elle prenne en compte la génération d'une courbe à l'aide de l'API GDI+ tout en se basant sur les valeurs retournées par le WebService. Le listing suivant illustre le code qui a été rajouté pour réaliser cette tâche. Encore une fois, par souci de simplicité, cinq variables ont été déclarées afin de stocker le cours aux instants T, T+1, T+2, jusqu'à T+5. Dans la pratique, l'utilisation d'une collection s'avère plus adaptée.

 

(...)
 protected override void OnPaint(PaintEventArgs e) {
	    Graphics g = e.Graphics;
	    Point[] pointarray=new Point[5];
			
			// Appel cinq fois du WebService Service1() 
			// Le procédé est un peu lourd mais ce n'est pas le plus important
			this.Value1=(new Service1().GetStockRate()).ToString();
			this.Value2=(new Service1().GetStockRate()).ToString();
			this.Value3=(new Service1().GetStockRate()).ToString();
			this.Value4=(new Service1().GetStockRate()).ToString();
			this.Value5=(new Service1().GetStockRate()).ToString();

			float a=Convert.ToInt32(this.Value1.ToString());
			a=a*3;
			a=a/2;
			a=150-a;

			float b=Convert.ToInt32(this.Value2.ToString());
			b=b*3;
			b=b/2;
			b=150-b;

			float c=Convert.ToInt32(this.Value3.ToString());
			c=c*3;
			c=c/2;
			c=150-c;

			float d=Convert.ToInt32(this.Value4.ToString());
			d=d*3;
			d=d/2;
			d=150-d;

			float f=Convert.ToInt32(this.Value5.ToString());
			f=f*3;
			f=f/2;
			f=150-f;

			Pen newpen = new Pen(Color.Black);
			//Set point of origin to be 50,50 on the form
			g.TranslateTransform(50,50);
			
			g.DrawLine(newpen,1,1,1,150);
			g.DrawLine(newpen,1,150,150,150);
		
			//Draw X and y axis

			g.DrawLine(newpen,-3,30,3,30);
			g.DrawLine(newpen,-3,60,3,60);
			g.DrawLine(newpen,-3,90,3,90);
			g.DrawLine(newpen,-3,120,3,120);
			g.DrawLine(newpen,30,147,30,153);
			g.DrawLine(newpen,60,147,60,153);
			g.DrawLine(newpen,90,147,90,153);
			g.DrawLine(newpen,120,147,120,153);
			g.DrawLine(newpen,150,147,150,153);

			Font newfont=new Font("Arial",10);
			//label Y axis
			g.DrawString("100",newfont,new SolidBrush(Color.Black),-25,-5);
			g.DrawString("80",newfont,new SolidBrush(Color.Black),-25,25);
			g.DrawString("60",newfont,new SolidBrush(Color.Black),-25,55);
			g.DrawString("40",newfont,new SolidBrush(Color.Black),-25,85);
			g.DrawString("20",newfont,new SolidBrush(Color.Black),-25,115);
				
			//Label X axis
			g.DrawString("0",newfont,new SolidBrush(Color.Black),-10,145);
			g.DrawString("p1",newfont,new SolidBrush(Color.Black),25,155);
			g.DrawString("p2",newfont,new SolidBrush(Color.Black),55,155);
			g.DrawString("p3",newfont,new SolidBrush(Color.Black),85,155);
			g.DrawString("p4",newfont,new SolidBrush(Color.Black),115,155);
			g.DrawString("p5",newfont,new SolidBrush(Color.Black),145,155);
			
			//Plot points
			g.DrawEllipse(new Pen(Color.Blue),30-2,a-2,4,4);
			g.DrawEllipse(new Pen(Color.Red),60-2,b-2,4,4);
			g.DrawEllipse(new Pen(Color.Green),90-2,c-2,4,4);
			g.DrawEllipse(new Pen(Color.Black),120-2,d-2,4,4);
			g.DrawEllipse(new Pen(Color.Gold),150-2,f-2,4,4);
			
			//Join the points
			pointarray[0]=new Point(30,Convert.ToInt32(a));
			pointarray[1]=new Point(60,Convert.ToInt32(b));
			pointarray[2]=new Point(90,Convert.ToInt32(c));
			pointarray[3]=new Point(120,Convert.ToInt32(d));
			pointarray[4]=new Point(150,Convert.ToInt32(f));		
			newpen = new Pen(Color.FromArgb(150, Color.Purple), 5);
			g.DrawLines(newpen,pointarray);		

        }

 

L'exemple précédent récupère à chaque appel de la méthode OnPaint() cinq valeurs entières aléatoires entre 0 et 100 en appelant le WebService service1 et la méthode GetStockRate(). Ensuite, un mécanisme de tracé de courbe utilisant GDI+ permet de représenter la progression du cours.

La figure suivante nous montre l'affichage résultant du chargement de l'applet dans Internet Explorer.

 

Et voilà, nos chers Gurus ont enfin réussi à implémenter leur application. Bien sûr, elle est perfectible, excepté l'affichage qui peut être amélioré, il restera à gérer le click de la souris pour afficher des informations détaillées à l'utilisateur. Les évènements .NET pourront être utilisés à ces fins avec les méthodes OnClick(), OnValueChanged(), ... 

Enfin, une vrai applet de type IntraDay chargée de récupérer en temps réel les cours doit être basée sur le Design Pattern Observer/Observable. Or, les WebServices utilisent le protocole HTTP pour communiquer et ce protocole est totalement inadapté à la gestion des callbacks (mode déconnecté).  A l'avenir, nous pourrions très bien imaginer un serveur .NET Remoting contrôlant des clients applets avec une notification asynchrone en cas d'évolution de la valeur.

Applet .NET et WebService Java

Le WebService utilisé dans cet exemple a été écrit en VB.NET mais il aurait été aussi facile de l'implémenter en Java. Cela n'aurait strictement rien changé dans la manière d'opérer concernant l'applet. Vous auriez utilisé l'outil wsdl.exe en référençant une URL pointant vers un serveur Glue, Apache SOAP ou SunOne. Ce sera, pourquoi pas l'objet d'un futur article.

De plus, le serveur Web joue, dans ce cas des applets .NET simples, un rôle presque négligeable. Rien ne vous empêche de mettre en lieu et place d'IIS un serveur web Apache ou Netscape, cela marchera aussi bien. 

Vider le cache d'applet du navigateur

Pendant les phases de développement, vous êtes fréquemment amenés à modifier l'implémentation de votre applet ou l'interface de votre WebService. Le navigateur, dans la plupart des cas, cache les Assemblies téléchargées dans le GAC (Global Assembly Cache) du poste client afin d'éviter les téléchargements inutiles. Pour vider ce cache explicitement, il vous suffit de taper :

gacutil /cdl

Et pour afficher la liste des DLL présentes dans le cache, tapez :

gacutil /ldl

 

Sécurité

Les applets .NET sont soumises aux même règles de sécurité qu'une applet Java. Elles n'ont pas la possibilité d'accéder aux ressources de la machine locale qui les exécute et ne peuvent interagir avec d'autres applications ne provenant pas du serveur à partir duquel elles ont été téléchargées. Dans notre étude de cas, si le client applet peut invoquer un WebService, c'est dû au fait que le WebService est situé sur le serveur hébergeant l'applet. Nous reviendrons sur ce sujet primordial dans d'autres articles avec la configuration d'applets .NET signées et installées dans le GAC (Global Assembly Cache). Ce sera l'occasion d'apprendre à utiliser l'outil CASPOL de .NET.

 

Conclusion

Les Applets .NET et Java sont très similaires. Nous avons vu à travers cet exemple que les mécanismes existants de part et d'autre se rapprochent beaucoup dans leur implémentation et dans leur manière d'aborder le déploiement. Quant aux performances, elles sont à peu près équivalentes avec un léger avantage pour .NET, la CLR utilisant un JIT. Enfin, si en Java une applet est considérée comme un contrôle spécial, en .NET, l'applet est réellement du type System.WinForms.Control

Télécharger les programmes sources

ChartControl.cs

Fichier Zip complet

 

Auteur : Sami JABER