4D serveur applicatif▲
Modes de fonctionnement habituels▲
Les développeurs 4D considèrent habituellement trois modes principaux de fonctionnement d’un moteur 4D :
1. En application monoposte ;
2. En architecture client/serveur connectée (c’est-à-dire que toute interruption de la connexion entre client et serveur entraîne une interruption du fonctionnement) ;
3. En serveur Web : le serveur web de 4D publiant des pages semi-dynamiques HTML à destination d’un navigateur.
On rencontre également des clients 4D utilisés en frontal d’un autre moteur de base de données, par exemple MySQL, au travers d’un plugin natif.
Un autre mode de fonctionnement commence à se faire une place : l’architecture orientée service (SOA) où 4D communique avec d’autres applicatifs au travers de requêtes web service.
Un autre frontal pour 4D▲
Cas où 4D Client n’est pas une solution▲
En dehors des pages HTML dans un navigateur, il demeure assez rare de concevoir une architecture 4D où le frontal ne soit pas une application 4D. Cela se justifie par l’excellente intégration du client/serveur 4D et sa grande facilité de déploiement et d’administration.
Cette approche trouve cependant ses limites dans deux cas principaux :
1. lorsque 4D Client n’est pas disponible pour la plateforme cible : cas des périphériques mobiles qui nous intéresse dans cette note, mais également des machines basées sur Linux ou Unix non OSX ;
2. lorsque le réseau n’offre pas une qualité suffisante pour permettre au protocole propriétaire de 4D de fonctionner correctement : cas de nombreuses liaisons ADSL.
Fonctionnement en mode semi-connecté▲
Une architecture permet de répondre à ces cas problématiques : recourir au serveur http de 4D pour transformer 4D en serveur applicatif. Comme dans le cas d’un serveur Web, on utilise le serveur http. Cependant les réponses du serveur ne renvoient plus des pages HTML, mais de l’information destinée à être exploitée par une autre application qui jouera le rôle de frontal. La tendance est aujourd’hui d’utiliser XML pour formater les échanges de données de manière à les rendre universellement exploitables.
Architecture de notre exemple▲
Voici un schéma illustrant cette approche dans l’exemple présenté dans cette note :
Séparation des couches▲
Il est important de noter que, dans cette architecture, l’intégralité de la présentation est assurée par le client Flash. Les messages échangés avec 4D ne comprennent que des données et aucune indication de présentation. En changeant simplement la partie cliente, on peut changer la couche présentation sans retoucher à la couche serveur.
La dissociation des couches est garante d’une bonne maintenabilité et évolutivité de la solution.
Interface virtuelle dans la méthode Sur connexion Web▲
Deux modes d’invocation de code▲
Les échanges de requêtes http sont gérés par la couche serveur http de 4D, la même que celle permettant le fonctionnement comme serveur Web. Cependant, les réponses renvoyées ne contiendront pas des pages HTML, mais des données formatées en XML.
Rappelons qu’une requête http peut appeler du code 4D de deux manières différentes :
· En invoquant directement une méthode par la balise url 4DACTION/ ;
· En passant par une couche virtuelle d’aiguillage, approche retenue dans notre exemple.
Méthode sur connexion web▲
Lorsqu’une requête http entrante n’invoque pas directement une ressource existante (page HTML du dossier Web, méthode 4D,…), la méthode base Sur connexion web est appelée et reçoit divers paramètres dont en $1 l’url appelée. Il est alors facile dans une structure « Au cas ou » de traiter la requête entrante en fonction de l’url demandée et d’appeler le code 4D correspondant.
Voici notre code :
Si
(
Vrai
)
`les paramètres entrants
C_TEXTE
(
$1
;
$2
;
$3
;
$4
;
$5
;
$6
)
C_TEXTE
(
$_vt_URL
)
$_vt_URL
:=
$1
C_TEXTE
(
$utilisateur
;
$motPasse
;
$IPBrowser
;
$IPServer
)
$utilisateur
:=
$5
$motPasse
:=
$6
$IPBrowser
:=
$3
$IPServer
:=
$4
Fin de si
COMPILER_WEB
Si
(
http_ve_siDebug=
1
)
HTTP_LireCorpsVersPressePapiers
Fin de si
C_BOOLEEN
(
$_vb_pocketPc
)
$_vb_pocketPc
:=
HTTP_isPocketPC
resa_AppelerSurErreur `installation d’une gestion d’erreurs
Au cas ou
`__________________________________________________________________
: (
$1
=
"/rdv"
)
Si
(
$_vb_pocketPc
)
ENVOYER FICHIER HTML(
"rdv_ppc.html"
)
Sinon
ENVOYER FICHIER HTML(
"rdv.html"
)
Fin de si
`__________________________________________________________________
: (
$1
=
"/get_Rdvs@"
)
resa_getListe (
$1
)
`__________________________________________________________________
: (
$1
=
"/add_Rdv@"
)
resa_addRecord (
$1
)
` _____________________________________________________________
: (
$1
=
"/favicon.ico"
)
|
(
$1
=
"favicon.ico"
)
ENVOYER TEXTE HTML(
""
)
`__________________________________________________________________
Sinon
HTTP_LireCorpsVersPressePapiers
TRACE
Fin de cas
Quelques commentaires :
Une variable http_ve_siDebug permet de basculer dans un mode debug où le contenu de la requête entrante est copié dans le presse-papiers. La variable est typée en entier afin de permettre une extension future, par exemple pour qu’une valeur différente provoque la journalisation des requêtes dans un fichier texte externe ;
La fonction http_isPocketPC analyse les champs d’entête http afin de déterminer si la requête entrante provient d’un PocketPC. Exemple de requête émise par un PocketPC :
GET/pp HTTP/11
Accept:*/*
Accept-Language: fr
UA-OS: Windows CE(Pocket PC)-Version 4.21
UA-color: color16
UA-pixels: 240x240
UA-CPU: Intel(R)PXA272
UA-Voice: TRUE
UA-Language: JavaScript
Accept-Encoding: gzip, deflate
User-Agent: Mozilla
Cette fonction exploite le champ UA-OS. Notez que le champ UA-pixels permettrait d’envoyer deux clips Flash différents suivant la résolution de l’écran du périphérique mobile.
En exploitant la fonction http_isPocketPC, il est possible de servir une page différente (et donc un clip Flash différent) suivant que le client soit un PocketPC ou un ordinateur de bureau.
API de l’exemple▲
Notre API propose trois entrées matérialisées par des requêtes :
· /rdv : renvoie une page HTML contenant le clip Flash ;
· /get/Rdvs/ : renvoie la liste des rendez-vous d’une date donnée, méthode resa_getListe ;
· /add_Rdv/ : demande l’ajout d’un rendez-vous, méthode resa_addRecord.
Les requêtes inconnues sont ici tracées en mode interprété pour faciliter le débogage, tandis que les requêtes de navigateurs demandant des favicons (favicon.ico) ne renvoient rien.
Liste des rendez-vous▲
Méthode resa_getListe▲
La méthode resa_getListe est appelée par Sur connexion Web.
` Methode : resa_getListe
` Description :
` reçoit une date et renvoie la liste des réservations pour cette date
`
` Parametres :
` $1:TEXTE:URL (/get_Resas/?date=22/5/2006)
`
` Version : 1
` Appel : resa_getListe (url)
` ----------------------------------------------------
C_TEXTE
(
$1
)
C_TEXTE
(
$_vt_faultCode
)
$_vt_faultCode
:=
""
C_TEXTE
(
$_vt_faultString
)
$_vt_faultString
:=
""
C_TEXTE
(
$_vt_param
)
$_vt_param
:=
http_getVar (
"Date"
)
Si
(
$_vt_param
=
""
)
`cas de Flash : passage de param par XML
`<getRdv><date>Tue May 30 00:00:00 GMT+0200 2006</date></getRdv>
C_TEXTE
(
$_vt_refXML
)
$_vt_refXML
:=
HTTP_DOM_loadXMLfromPOST
$_vt_param
:=
DOM_Lire_Valeur_Element_XPath (
$_vt_refXML
;
"/getRdv/Date"
)
Fin de si
C_DATE
(
$_vd_dateResa
)
$_vd_dateResa
:=
Date
(
$_vt_param
)
Si
(
$_vd_dateResa
#
!00/00/00!
)
LECTURE SEULEMENT
([
EVENEMENT])
CHERCHER
([
EVENEMENT];[
EVENEMENT]
Date
_evenement=
$_vd_dateResa
)
TRIER
([
EVENEMENT];[
EVENEMENT]
Heure
_evenement;>)
C_BLOB
(
$_vx_blob
)
resa_versXML (->
$_vx_blob
)
REDUIRE SELECTION
([
EVENEMENT];
0
)
LIBERER ENREGISTREMENT
([
EVENEMENT])
HTTP_responseByBLOB (->
$_vx_blob
;
"text/xml"
)
Sinon
$_vt_faultCode
:=
"client"
$_vt_faultString
:=
"Il faut renseigner la date"
HTTP_DOM_sendFault (
$_vt_faultCode
;
$_vt_faultString
)
Fin de si
Données entrantes▲
La requête entrante véhicule la date pour laquelle construire la liste des rendez-vous sous forme d’une structure XML :
<getRdv>
<date>la date</date>
</getRdv>
La fonction HTTP_DOM_loadXMLfromPOST permet de charger dans un arbre DOM une telle structure XML passée dans les données de la requête, par une méthode POST. Elle retourne la référence de la racine de l’arbre.
` Methode : HTTP_loadXMLfromPOST
` Description :
` chargement du XML passé en POST ds un arbre DOM
`
` Parametres :
` $0:TEXTE:Ref Element cree
`
` Version : 1
` Appel : Ref Element cree :=HTTP_loadXMLfromPOST
` ----------------------------------------------------
C_TEXTE
(
$0
)
$0
:=
""
C_BLOB
(
$_vx_BLOBXML
)
LIRE CORPS HTTP(
$_vx_BLOBXML
)
Si
(
http_ve_siDebug=
1
)
UTF8_vers_ISO_blob (->
$_vx_BLOBXML
)
ZBLOB_versPressePapiers (->
$_vx_BLOBXML
)
Fin de si
Si
(
Taille BLOB
(
$_vx_BLOBXML
)>
0
)
$0
:=
DOM Analyser variable XML
(
$_vx_BLOBXML
)
Si
(
ok=
0
)
$0
:=
""
Fin de si
Fin de si
Reste à récupérer la valeur recherchée par la fonction DOM_Lire_Valeur_Element_XPath qui retourne la valeur d’un élément désigné par sa notation XPath :
` Methode : DOM_Lire_Valeur_Element_XPath
` Description :
` relire une valeur simple XML par une expression XPath
`
` Parametres :
` $0:TEXTE:valeur simple XML relue
` $1:TEXTE:Ref elt XML de depart
` $2:TEXTE:Expression Xpath
`
` Version : 1
` Appel : $_vt_personne:=DOM_Lire_Valeur_Element_XPath ($_vt_RefXML;"/root/personne")
` ----------------------------------------------------
C_TEXTE
(
$1
;
$_vt_RefXML
)
$_vt_RefXML
:=
$1
C_TEXTE
(
$2
;
$_vt_XPath
)
$_vt_XPath
:=
$2
C_TEXTE
(
$0
)
$0
:=
""
Si
(
$_vt_RefXML
#
""
)
$_vt_RefElement
:=
DOM Chercher element XML
(
$_vt_RefXML
;
$_vt_XPath
)
Si
(
ok=
1
)
DOM LIRE VALEUR ELEMENT XML
(
$_vt_RefElement
;
$0
)
Fin de si
Fin de si
Ici, l’expression XPath vaut « /getRdv/date », c’est-à-dire l’élément <date> enfant de la racine <getRdv>.
Données sortantes▲
Si la date est non-vide, nous recherchons les rendez-vous ou événements correspondants, puis nous transformons la sélection correspondante en structure XML afin de la retourner au client Flash.
La méthode resa_versXML créé une structure XML en utilisant les commandes DOM depuis une sélection de [EVENEMENT] et la retourne dans un BLOB.
La méthode HTTP_responseByBLOB renvoie le BLOB passé en paramètre (par pointeur pour économiser la mémoire) dans la réponse http. Le type MIME des données contenues dans le BLOB est passé en deuxième paramètre, il vaut « text/xml » pour une structure XML.
` Methode : HTTP_responseByBLOB
` Description :
` envoie la réponse à la requête HTTP
`
` Parametres :
` $1:POINTEUR:BLOB à envoyer
` $2:TEXTE:type MIME, par ex "application/x-shockwave-flash" ou "text/html"
`
` Version : 2
` le 24/05/06, 08:32:45
`
` Appel : HTTP_responseByBLOB (->$_vx_swf;"application/x-shockwave-flash")
` ----------------------------------------------------
C_POINTEUR
(
$1
)
`BLOB
C_TEXTE
(
$2
;
$_vt_MIME
)
$_vt_MIME
:=
$2
C_ENTIER(
http_ve_siDebug)
Si
(
http_ve_siDebug=
1
)
ZBLOB_versPressePapiers (
$1
)
Fin de si
ENVOYER BLOB HTML(
$1
->;
$_vt_MIME
)
Gestion des erreurs▲
En cas d’erreur, un flux XML est renvoyé par la routine HTTP_DOM_sendFault en respectant la structure suivante :
<response>
<faultstring>texte de l’erreur<faultstring/>
vfaultcode>code de l’erreur<faultcode/>
</response>
Ajout d’un rendez-vous▲
Traitement de la requête▲
La méthode resa_addRecord est appelée par Sur connexion Web.
` ----------------------------------------------------
` Nom utilisateur (OS) : christophe Keromen
` Date et heure : 27/05/06, 08:06:06
` ----------------------------------------------------
` Methode : resa_addRecord
` Description :
` ajout d’une réservation depuis le client lz
`
` Parametres :
` passé par POST
`
` Version : 3
` le 21/11/06, 13:06:28
` le 09/06/06, 09:23:37
`
` Appel : resa_addRecord
` ----------------------------------------------------
C_TEXTE
(
$1
)
C_TEXTE
(
$_vt_desc
)
C_TEXTE
(
$_vt_lib
)
C_TEXTE
(
$_vt_hour
)
C_TEXTE
(
$_vt_date
)
C_TEXTE
(
$_vt_End
)
`requête depuis Flash : data en XML
`<add_Rdv><date>02/06/2006</date><libelle>Formation Flash</libelle>
`<heure>9:00</heure><description>Init 3 participants</description></add_Rdv>
C_TEXTE
(
$_vt_refXML
)
$_vt_refXML
:=
HTTP_DOM_loadXMLfromPOST
Si
(
ok=
1
)
&
(
$_vt_refXML
#
""
)
$_vt_date
:=
DOM_Lire_Valeur_Element_XPath (
$_vt_refXML
;
"/add_Rdv/Date"
)
$_vt_lib
:=
DOM_Lire_Valeur_Element_XPath (
$_vt_refXML
;
"/add_Rdv/libelle"
)
$_vt_hour
:=
DOM_Lire_Valeur_Element_XPath (
$_vt_refXML
;
"/add_Rdv/Heure"
)
$_vt_desc
:=
DOM_Lire_Valeur_Element_XPath (
$_vt_refXML
;
"/add_Rdv/description"
)
C_DATE
(
$_vd_date
)
$_vd_date
:=
Date
(
$_vt_date
)
C_HEURE
(
$_vh_heure
)
$_vh_heure
:=
Heure
(
$_vt_hour
)
C_HEURE
(
$_vh_heureFin
)
$_vh_heureFin
:=
Heure
(
$_vt_End
)
C_TEXTE
(
$_vt_faultCode
)
$_vt_faultCode
:=
""
C_TEXTE
(
$_vt_faultString
)
$_vt_faultString
:=
""
Si
(
$_vd_date
#
!00/00/00!
)
&
(
$_vh_heure
#
?00:00:00?
)
C_ENTIER LONG
(
$_vl_ts
)
$_vl_ts
:=
zgen_getTimeStamp (
$_vd_date
;
$_vh_heure
)
C_ENTIER LONG
(
$_vl_count
)
FIXER DESTINATION RECHERCHE
(
Vers variable
;
$_vl_count
)
CHERCHER
([
EVENEMENT];[
EVENEMENT]
Jour_TS=
$_vl_ts
)
FIXER DESTINATION RECHERCHE
(
Vers sélection courante
)
Si
(
$_vl_count
=
0
)
CREER ENREGISTREMENT
([
EVENEMENT])
[
EVENEMENT]
Date
_evenement:=
$_vd_date
[
EVENEMENT]
Description:=
$_vt_desc
[
EVENEMENT]
Heure
_evenement:=
$_vh_heure
[
EVENEMENT]
Fin_evenement:=
$_vh_heureFin
[
EVENEMENT]
Libelle:=
$_vt_lib
STOCKER ENREGISTREMENT
([
EVENEMENT])
LIBERER ENREGISTREMENT
([
EVENEMENT])
Si
(
resa_vt_error#
""
)
`erreur ds le trigger par ex.
$_vt_faultCode
:=
"server"
$_vt_faultString
:=
resa_vt_error
Fin de si
Sinon
$_vt_faultCode
:=
"client"
$_vt_faultString
:=
"Il existe déjà un événement à cette date et cette heure"
Fin de si
Sinon
$_vt_faultCode
:=
"client"
$_vt_faultString
:=
"Il faut renseigner date et heure"
Fin de si
Sinon
$_vt_faultCode
:=
"client"
$_vt_faultString
:=
"Les paramètres passés ne sont pas corrects"
Fin de si
C_TEXTE
(
$_vt_ref_XML
)
$_vt_ref_XML
:=
HTTP_DOM_creer_ref_XML (
"response"
)
Si
(
$_vt_faultCode
=
""
)
DOM_AJOUTER_ELEMENT_SIMPLE (
$_vt_ref_XML
;
"result"
;
"true"
)
`true
`<?xml version="1.0" encoding="UTF-8" standalone="yes" ?><response><result>true</result></response>
Sinon
HTTP_DOM_AddFault (
$_vt_ref_XML
;
$_vt_faultString
;
$_vt_faultCode
)
Fin de si
HTTP_DOM_envoyer_XML (
$_vt_ref_XML
)
Le principe est le même que pour la méthode précédente.
Gestion des erreurs▲
Exemple d’implémentation de règle de gestion en trigger, la méthode resa_trigger est appelée dans le trigger de la table [EVENEMENT] :
C_ENTIER LONG
(
$_vl_event
)
$_vl_event
:=
Evenement moteur
C_ENTIER LONG
(
$0
)
$0
:=
resa_trigger (
$_vl_event
)
` Elle remonte une erreur si l’on tente d’enregistrer un événement en dehors des plages autorisées :
` Methode : resa_trigger
` Description :
` traitement en trigger de la table [RESERVATION]
`
` Parametres :
` $0:ENTIER LONG:erreur
` $1:ENTIER LONG:evenement moteur
`
` Version : 1
` Appel : erreur:=resa_trigger(evenement moteur )
` ----------------------------------------------------
C_ENTIER LONG
(
$1
;
$_vl_event
)
$_vl_event
:=
$1
C_ENTIER LONG
(
$0
)
$0
:=
0
Au cas ou
: (
$_vl_event
=
Sur sauvegarde enregistrement
)
|
(
$_vl_event
=
Sur sauvegarde nouvel enreg
)
[
EVENEMENT]
Jour_TS:=
zgen_getTimeStamp([
EVENEMENT]
Date
_evenement;
[
EVENEMENT]
Heure
_evenement)
Si
([
EVENEMENT]
PK_evenement=
0
)
[
EVENEMENT]
PK_evenement:=
Numerotation automatique
([
EVENEMENT])
Fin de si
`ex. de regle de gestion
Si
([
EVENEMENT]
Heure
_evenement<
?07:00:00?
)
|
([
EVENEMENT]
Heure
_evenement>
?21:00:00?
)
$0
:=-
32000
Fin de si
Fin de cas
L’erreur remontée dans le trigger est capturée par le gestionnaire d’événement resa_handle_error, installé par resa_AppelerSurErreur dans la méthode Sur Connexion Web. Le gestionnaire positionne un code et un message d’erreur :
` Methode : resa_handleError
` Description :
` Gestionnaire d’erreurs des réservations
`
` Parametres :
`
`
` Version : 1
` Appel : resa_handleError
` ----------------------------------------------------
C_ENTIER LONG
(
resa_vl_error)
resa_vl_error:=
error
Au cas ou
: (
resa_vl_error=-
32000
)
resa_vt_error:=
"L’heure de réservation doit être comprise entre 07h et 21h"
Sinon
resa_vt_error:=
"Erreur inconnue "
+
Chaine
(
resa_vl_error)
Fin de cas
Si une erreur est détectée, la réponse http renvoie un faultCode et une faultString en respectant la structure décrite précédemment.
Comparaison avec SOAP▲
Structure propriétaire▲
Les échanges de message sont très semblables à des échanges SOAP. Cependant, la structure des messages est propriétaire, on n’y trouve pas d’enveloppe SOAP contenant un BODY contenant le message. Le client doit donc connaître la structure propriétaire pour fonctionner : pas d’interopérabilité. Pas non plus de description par un fichier WSDL à la structure formalisée. Il vous revient de documenter l’API de votre serveur applicatif.
Meilleure prise en compte des structures XML▲
En revanche, travaillant au plus près des échanges, le développeur maîtrise mieux le contenu de ceux-ci. En particulier, devenant responsable du codage et décodage des informations en XML, il peut éviter certains aspects automatiques de la couche SOAP de 4D non adaptés aux structures XML complexes :
· encodage base64 superflu ;
· traitement des structures XML comme du texte et application des règles de syntaxe XML, par exemple transformation des <> en entités.
Vitesse accrue▲
La couche SOAP entraîne une consommation de ressources supplémentaires qu’il est précieux de pouvoir économiser sur un périphérique de faible puissance CPU comme le PocketPC.
Côté client Flash▲
Nous n’allons pas ici entamer une formation à Flash. Nous nous contenterons de rappeler les principes de développement côté Flash.
Composants▲
La réalisation d’interface dans Flash s’appuie sur l’utilisation de composants.
Interface▲
On voit sur la droite (palette Components) un extrait de la liste des composants d’interface (User Interface) proposés par Flash.
Dans notre exemple, sont utilisés les composants d’interface suivants :
· TextInput : saisie de la date ;
· DataGrid : liste des rendez-vous ;
· TextField : affichage de texte dynamique.
Données▲
Ces composants reçoivent leurs données de composants dédiés, présents dans notre copie d’écran sur fond gris :
· un composant XMLconnector se charge des échanges http véhiculant du XML ;
· un composant DataSet joue le rôle de conteneur des données relues depuis la source XMLconnector.
Liaisons▲
Des liaisons peuvent être établies par l’inspecteur de composants (palette Component Inspector en bas à droite de notre copie d’écran).
On voit dans notre exemple que l’instance du composant d’interface DataGrid nommée rdvListe_dg est liée (bound to) au composant de donnée rdvListe_ds, instance du composant DataSet, par la propriété dataProvider (fournisseur de données).
Ces liaisons sont mises en œuvre automatiquement par Flash dès qu’un événement de mise à jour est détecté.
De son côté le composant DataSet est lié au composant XMLConnector. Ainsi, lorsqu’une requête http est traitée par le composant XMLConnector, celui-ci notifie le composant DataSet qui reçoit les données et les fait suivre, après d’éventuelles transformations, aux composants d’interface abonnés. Ici la dataGrid. Le même mécanisme est mis en jeu entre la dataGrid et le composant TextField qui affiche la description d’un rendez-vous dès qu’une ligne est sélectionnée dans la liste.
Programmation▲
Séparation du comportement▲
Quoique ce mécanisme de liaisons apporte un automatisme de mise en œuvre important, il demeure cependant nécessaire d’écrire du code. Une bonne pratique de programmation consiste à ne pas l’inclure dans l’équivalent des méthodes objet des éléments d’interface, mais à définir une classe de gestion du formulaire qui assurera la prise en compte du comportement.
ActionScript▲
Ces classes sont écrites en ActionScript, le langage propriétaire de Flash, une mouture du langage ECMAScript, standard dont est issu également JavaScript :
https://fr.wikipedia.org/wiki/ECMAScript
Ainsi, pour un développeur maîtrisant la syntaxe JavaScript, le passage vers ActionScript ne posera pas de difficulté.
Chaque classe est enregistrée dans un fichier texte dédié suffixé par .as.
Édition du code ActionScript▲
Le code ActionScript peut ainsi être édité dans des outils indépendants de l’IDE fourni par Adobe. J’utilise l’excellent SE|PY écrit en Python, bien plus pratique que l’éditeur natif de Flash :
Site web de SE|PY : http://www.sephiroth.it/python/sepy.php
Pour plus d’informations sur Flash et le PocketPC :
https://www.adobe.com/devnet/devices/pocket_pc.html
Conclusion▲
Nous avons détaillé dans cette note la réalisation d’une interface applicative http-XML dans 4D, pouvant être considérée comme une alternative propriétaire à SOAP. Nous avons également présenté les concepts de réalisation de la partie cliente dans Adobe Flash.
La combinaison des deux outils, sans égaler la simplicité et la rapidité de réalisation d’une solution purement 4D, permet néanmoins de répondre à des cas de figure complémentaires :
· réseau lent ou mode semi-connecté (ADSL, GPRS, Edge) ;
· OS non supporté par 4D : Linux ;
· périphériques non supportés par 4D : PocketPC, smartphones…
Télécharger la base exemple▲
Télécharger la base exemple.