I. Introduction▲
Avec Delphi il est possible de développer des applications web fondées sur l'architecture XML/XSL que je présente dans cet article. Kylix 2 apporte lui aussi le Framework WebSnap permettant de faciliter l'écriture d'application orientée web. Seulement voilà, le hic c'est que les composants XMLBroker et XSLPageProducer n'apparaissent pas dans la palette de composants de Kylix 2 ! Ceci vient du fait Borland n'as pas interfacé de processeur XSL pour le composant TXMLDocument. Delphi 6 s'appuie sur le processeur de Microsoft (msxml3.dll) qui n'est bien sûr pas disponible sous Linux. Il existe cependant des processeurs XSL sous Linux: Celui d'Oracle mais ne gérant pas le XPath, celui de Xalan (écrit en C++), libxslt qui est une bibliothèque pour Gnome et enfin Sablotron que nous allons utiliser dans cet article. Tous n'implémentent pas forcément entièrement la norme XSL du W3C mais peuvent être suffisant pour faire les transformations de bases.
Dans cet article, je vais montrer comment développer une application web XML/XSL avec WebSnap similaire a ce que l'on peut faire avec Delphi 6. Les composant XMLBroker et XSLPageProducer ne sont pas disponibles dans la palette de composants mais le code source est fourni. On mettra aussi en évidence un problème de parseur XML concernant ce composant.
Remarque : On utilisera ce qui a été vu dans l'article concernant le composant TXMLDocument.
II. Le composant XMLBroker▲
Dans la version Delphi de l'application WebSnap/XSL, on utilise le composant XMLBroker pour transformer les données extraites de la base de données en un flux XML. Ce composant n'est pas enregistré dans la palette de composant de Kylix 2, mais le code source est fourni (source/internet/XMLBrokr.pas). Il suffit donc de l'enregistrer comme si c'était notre propre composant :
unit XMLBrokReg;
interface
procedure Register;
implementation
uses
Classes, XMLBrokr;
procedure Register;
begin
RegisterComponents('WebSnap', [TXMLBroker]);
end;
end.
Puis d'ajouter ce fichier (XMLBrokReg.pas) dans un paquet de composant et de l'installer. Le composant TXMLBroker apparaîtra alors dans l'onglet WebSnap.
III. Le composant XSLPageProducer▲
Pour le composant TXSLPageProducer, ce n'est pas aussi simple que pour le TXMLBroker. En effet si on enregistre de la même façon ce composant on va tomber sur un problème lié au code du composant. Le TXSLPageProducer dérive du composant TXMLDocument et ajoute des méthodes permettant de transformer un flux XML avec un fichier XSL. Le composant charge dans sa structure interne (TXMLDocument) le fichier XSL qu'on lui a associé. Puis lors de l'appel aux méthodes de transformations, une nouveau TXMLDocument est créé s'occupant de parser le flux XML. Voici le code extrait du fichier XSLProd.pas (source/internet/XSLProd.pas) :
function TCustomXSLPageProducer.GetXMLDocument(out AXMLDocument: IXMLDocument): Boolean;
var
Stream: TStream;
Owned: Boolean;
begin
Result := Supports(IUnknown(XMLData), IXMLDocument, AXMLDocument);
if Result then
if (csDesigning in ComponentState) and (AXMLDocument.FileName <> '') then
begin
// Load the file when designing
Result := False;
AXMLDocument := nil;
end;
if not Result then
begin
Owned := False;
Stream := GetXMLData(Owned);
if Stream <> nil then
try
AXMLDocument := TXMLDocument.Create('');
try
AXMLDocument.LoadFromStream(Stream);
except
FreeAndNil(AXMLDocument);
raise;
end;
Result := AXMLDocument <> nil;
finally
if Owned then
Stream.Free;
end;
end;
end;
Le composant TXMLDocument est créé dynamiquement, mais le DOMVendor n'est pas indiqué. Ceci a pour conséquence que le flux XML sera traité par le parseur par défaut (variable DefaultDOMVendor. MSXML sous windows, OpenXML sous Linux) ce qui dans notre cas sera OpenXML. Malheureusement OpenXML ne gère pas le XSL et lors de la transformation, si on a indiqué Sablotron comme parseur pour le TXSLPageProducer, une erreur va survenir, car il y aura mélange des noeuds traités par OpenXML et ceux de Sablotron.
Il existe 2 solutions pour résoudre ce problème. La première consiste à modifier la variable globale DefaultDOMVendor définie et initialisée dans xmldom.pas. On pourra par exemple dans le OnCreate du WebPageModule principal de l'application écrire le code suivant :
procedure TListDepartment.WebPageModuleBeforeDispatchPage(Sender: TObject;
const PageName: String; var Handled: Boolean);
begin
DefaultDOMVendor := SSablot;
end;
La constante SSablot est définie dans l'unité sablotxmldom.pas. Mais cette solution ne peux fonctionner qu'à l'exécution. Impossible de voir le résultat à la conception.
La deuxième solution réside dans l'écriture d'un nouveau composant descendant de TXSLPageProducer. Il va ainsi être possible de corriger la méthode GetXMLDocument posant problème. Cette méthode n'est pas virtuelle donc impossible à redéfinir directement. Mais elle est appelée par ContentFromStream qui, par contre, est virtuelle. On va donc créer une nouvelle méthode GetXMLDocumentEx qui va être appelé par la méthode ContentfromStream que l'on aura redéfinie.
type
TXSLPageProducerEx = class(TXSLPageProducer, IProduceContent, IProduceContentFrom,
ISetAppDispatcher, IGetAppDispatcher, IProducerEditorViewSupport,
IGetProducerTemplate)
private
protected
function GetXMLDocumentEx(out AXMLDocument: IXMLDocument): Boolean;
procedure Loaded; override;
public
function ContentFromStream(InStream: TStream): string; override;
published
end;
function TXSLPageProducerEx.ContentFromStream(InStream: TStream): string;
var
XMLDocument: IXMLDocument;
W: WideString;
begin
if XMLData = nil then
RaiseNoXMLData;
if GetXMLDocumentEx(XMLDocument) then
begin
Self.LoadFromStream(InStream);
XMLDocument.Active := True;
if Self.DocumentElement = nil then
RaiseNoXMLDocument
else if XMLDocument.Node = nil then
RaiseNoXMLData;
XMLDocument.DocumentElement.TransformNode(Self.DocumentElement, W);
Result := W;
end
else
RaiseNoXMLDocument;
end;
function TXSLPageProducerEx.GetXMLDocumentEx(
out AXMLDocument: IXMLDocument): Boolean;
var
Stream: TStream;
Owned: Boolean;
XMLCompo: TXMLDocument;
begin
Result := Supports(IUnknown(XMLData), IXMLDocument, AXMLDocument);
if Result then
if (csDesigning in ComponentState) and (AXMLDocument.FileName <> '') then
begin
// Load the file when designing
Result := False;
AXMLDocument := nil;
end;
if not Result then
begin
Owned := False;
Stream := GetXMLData(Owned);
if Stream <> nil then
try
XMLCompo := TXMLDocument.Create('');
try
XMLCompo.DOMVendor := DOMVendor;
AXMLDocument := XMLCompo;
AXMLDocument.LoadFromStream(Stream);
except
FreeAndNil(AXMLDocument);
raise;
end;
Result := AXMLDocument <> nil;
finally
if Owned then
Stream.Free;
end;
end;
end;
Rajoutez le fichier XSLPageProducerEx.pas dans un paquet de composants (par exemple avec le XMLBrokReg.pas) et notre nouveau composant apparaîtra dans l'onglet WebSnap. Les modifications apportées à ce nouveau composant permettent de synchroniser le DOMVendor pour le fichier XSL et pour le flux XML. On pourra ainsi avoir la prévisualisation à la conception.
IV. Application WebSnap XML/XSL▲
Il nous est maintenant possible de réaliser notre application XML/XSL avec WebSnap similaire à celle que l'on a pu faire avec Delphi 6. La différence réside essentiellement dans l'emploi de dbExpress pour l'accès à la base de données (employee.gdb d'Interbase).
Pour les WebPageModules (ListDepartment_pgm et FormDepartment_pgm) il faut remplacer le XSLPageProducer qui est mis par défaut par notre XSLPageProducerEx. La propriété PageProducer du WebPageModule doit être mis à jour pour pointer vers le nouveau composant. Enfin on positionne le DOMVendor sur Sablotron.
Vous pouvez reprendre les fichiers XSL de votre application Delphi 6, ce sont les mêmes ! Vous constaterez alors que la transformation s'effectue parfaitement dans la fenêtre de prévisualisation :
V. Conclusion▲
Nous avons vu qu'il était possible avec Kylix 2, comme avec Delphi 6, de créer des applications web XML/XSL en utilisant WebSnap. Même si cela n'est pas évident de prime abord, une fois les bons composants enregistrés, c'est comme sous Windows. Le plus important reste l'interfaçage avec un parseur qui gère le XSL. pour plus d'information sur le sujet, je vous renvoie à mon article sur le composant TXMLDocument.
Voici les fichiers XMLBrokReg.pas et XSLPageProducerEx.pas permettant d'enregistrer les composants nécessaires au développement d'applications web XML/XSL avec Kylix 2, et l'interface de Sablotron pour le TXMLDocument.