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 :

 
Sélectionnez
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) :

 
Sélectionnez
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 :

 
Sélectionnez
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.

 
Sélectionnez
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;
 
Sélectionnez
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).

Image non disponible

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.

Image non disponible

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 :

Image non disponible

Image non disponible

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.