Date de création : | 1 Décembre 2003 | |
Dernière modification : | 1 Décembre 2003 | |
Diffusion : | internet |
Ce document a pour but d'expliquer comment créer un canal UPortal (utilisant interface IChanel ) qui fait appel à un Web Service. Lequel Web Service est compatible CAS ce qui implique que le canal utilise les capacités de UPortal à être un proxy CAS. De plus, le Web Services maintient une session avec le canal afin notamment de ne pas rejouer, à chaque appel, la validation PT (proxy Ticket) CAS.
Ce document est architecturé de la façon suivante :
Remarques :
Parmi les types de canaux proposés dans uPortal, celui qui offre le plus de possibilités (interaction avec les objets internes, utilisation du moteur de rendu XML, etc.) est le type Custom. C'est aussi le type de canal le plus compliqué à réaliser.
Cette section s'attache à décrire les grands principes de création d'un tel canal. Elle est très largement inspirée de la documentation de référence uPortal [1].
Figure 8 - Exemple de canal
Les fichiers java doivent être placés dans le répertoire source de uPortal sous la forme <nom_package>/CnomCanal.java. Le C en début du nom du canal est requis (ex : source/org/esupportail/uportal/channels/testws/CTestWS.java).
Les fichiers utilisés pour le rendu XML doivent être placés dans le répertoire webpages/stylesheets/<nom_package> de uPortal (ex : webpages/stylesheets/esupportail/uportal/channels/testws/normal.xsl).
L'utilisation de "ant deploy" permet de compiler la classe et de la positionner dans le contexte du serveur d'applications avec les fichiers de rendu XML liés.
Il est nécessaire d'importer certaines classes uPortal :
import org.jasig.portal.IChannel;
import org.jasig.portal.ChannelStaticData;
import org.jasig.portal.ChannelRuntimeData;
import org.jasig.portal.ChannelRuntimeProperties;
import org.jasig.portal.PortalEvent;
import org.jasig.portal.PortalException;
import org.jasig.portal.utils.XSLT;
import org.xml.sax.ContentHandler;
import java.util.Date;
La classe java doit "implémenter" IChannel.
Le constructeur de la classe est appelé une fois par session utilisateur uPortal dans la mesure où le canal est affiché au moins une fois.
Permet au canal de signaler au portail s'il doit s'afficher ou pas en renvoyant un objet ChannelRuntimeProperties. A la création de cet objet la propriété pilotant l'affichage est vrai par défaut.
Le portail informe le canal des actions de l'utilisateur en appelant cette méthode. Pour cela le portail passe un objet PortalEvent dont l'appel à la méthode getEventNumber permet au canal de savoir si l'utilisateur a cliqué sur l'un des boutons suivants : "about", "detach", "edit", "help" ou si l'utilisateur supprime le canal ou si l'utilisateur ferme sa session uPortal.
Par exemple, si l'on gère une page "about", c'est là que l'on va détecter que l'utilisateur demande son affichage. On mémorise alors l'information pour l'utiliser au moment de l'affichage par renderXML.
if (ev.getEventNumber() == PortalEvent.ABOUT_BUTTON_EVENT) {
mode = MODE_INFO;
}
ChannelStaticData permet de retrouver des informations sur la configuration du canal ou l'utilisateur courant.
Cette méthode est appelée aussitôt après le constructeur et peut servir, comme ce dernier, à initialiser des variables.
ChannelRuntimeData permet de retrouver des informations d'exécution comme les paramètres d'un lien cliqué dans le canal.
Cette méthode est appelée juste avant l'affichage.
Dans notre exemple, on s'en sert pour rechercher la date et l'heure courante quand l'utilisateur clique sur un lien "Mettre à jour" avec un href de type "?appel=true" :
this.runtimeData = rd;
if (runtimeData.getParameter("appel") != null) { //appel au service Web
try {
SimpleDateFormat tmp = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
retWS tmp.format(new Date());
}
catch (Exception e) {
System.out.println(e);
}
/
Cette méthode génère le XML qui sera traité par uPortal pour l'affichage.
Dans notre exemple où l'on gère deux affichages (un "normal" donnant la date et l'heure et un autre "info" quand on clique sur le bouton "about" de uPortal) :
xml = new StringBuffer();
if (mode == MODE_NORMAL) {
xml.append("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>");
xml.append("<ret>");
xml.append(retWS);
xml.append("</ret>");
stylesheet = "normal";
}
if (mode == MODE_INFO) {
xml.append("<?xml version=\"1.0\" encoding=\"iso-8859-1\"?>");
xml.append("<ret>");
xml.append("Canal d'appel d'un Web Service");
xml.append("</ret>");
stylesheet = "info";
}
La méthode initialise ensuite le rendu XML avec xslt.setXSL (Cf. paragraphe suivant). La fonction xslt.setStylesheetParameter permet de passer des paramètres à la feuille de style. Un de ces paramètres très important est l'URL du canal à l'intérieur de la page uPortal que l'on obtient grâce à ChannelRuntimeData.getBaseActionURL. Dans notre exemple, nous en avons besoin pour pouvoir insérer un lien "Mettre à jour" valide :
XSLT xslt = new XSLT(this);
xslt.setXML(xml.toString());
xslt.setXSL("CTestWS.ssl", stylesheet, runtimeData.getBrowserInfo()); xslt.setStylesheetParameter("baseActionURL", runtimeData.getBaseActionURL());
xslt.setTarget(out);
xslt.transform();
uPortal gère, grâce à xslt.setXSL, les feuilles de styles xslt différentes en fonction de trois paramètres principaux :
Dans notre exemple on a défini un fichier CTestWS.ssl :
<?xml-stylesheet
href="normal.xsl"
title="normal"
type="text/xsl"
media="netscape"
default="true"?>
<?xml-stylesheet
href="normal_explorer.xsl"
title="normal"
type="text/xsl"
media="explorer"?>
<?xml-stylesheet
href="info.xsl"
title="info"
type="text/xsl"
default="true"?>
Il ne reste plus qu'à écrire chaque feuille de style.
Exemple de normal_explorer.xsl :
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html" indent="no" />
<xsl:param name="baseActionURL"></xsl:param>
<xsl:template match="/">
Bonjour, Nous sommes le : <xsl:value-of select="ret" />
<p><a href="{$baseActionURL}?appel=true">Mettre à jour</a></p>
Vous utilisez Internet Explorer !!!!
</xsl:template>
</xsl:stylesheet>
Axis, en plus d'être une bibliothèque utilisable pour invoquer un Web Services côté client est aussi un environnement d'exécution de Web services.
C'est une chose assez simple. Elle se résume généralement à l'écriture d'une simple classe java. Le plus compliqué consiste à déposer cette classe dans l'environnement d'exécution de Axis.
Attention, quand je parle de l'environnement d'exécution de Axis, il ne faut pas voir Axis comme un nouveau serveur d'applications au même titre que Tomcat par exemple. Axis s'installe sur un serveur d'applications (Tomcat par exemple) et offre ensuite un espace d'exécution, des classes qu'on lui confie, sous forme de Web Services (gestion des WSDL, SOAP, etc.).
Le paragraphe 2 utilise un Web Service très simple dont voici le code source :
import java.util.Date;
import java.text.SimpleDateFormat;
public class WSTest {
public String hm() {
SimpleDateFormat tmp = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
return tmp.format(new Date());
}
}
Ce Web Service est déployé dans Axis simplement en :
C'est tout !
On peut ensuite invoquer le Web Service, comme mentionner au paragraphe 2, grâce à une ULR de type http://addr_srv_appli:port/axis/WSTest.jws.
L'appel de http://addr_srv_appli:port/axis/WSTest.jws dans un navigateur génère cet affichage :
Un clique sur "Click to see the WSDL" affiche le WSDL du Web Service.
Par contre, un Web Service, déployé sous la forme d'un fichier jws est limité. Dans notre cas, par exemple, il ne peut pas supporter les sessions. On est obligé d'aller plus loin.
En fait, ce n'est pas beaucoup plus complexe. En effet, pour gérer les sessions, il suffit de déployer la classe java manuellement dans AXIS à l'aide d'un fichier WSDD [2].
De plus, la classe java, est elle-même un peu plus complexe à écrire car elle gère l'identification CAS.
Voici le code source de la classe java modifiée
package org.esupportail;
import java.util.Date;
import java.text.SimpleDateFormat;
import edu.yale.its.tp.cas.client.ProxyTicketValidator;
public class WSTest2 {
private int numberOfCalls;
private String user = "????";
public WSTest2() {
numberOfCalls = 0;
}
public String login(String PT) {
try {
System.setProperty( "java.protocol.handler.pkgs",
"com.sun.net.ssl.internal.www.protocol");
ProxyTicketValidator pv = new ProxyTicketValidator();
pv.setCasValidateUrl("https://lscri.univ-rennes1.fr:8443/cas/proxyValidate");
pv.setService("http://localhost:8080/axis/services/WSTest2");
pv.setServiceTicket(PT);
pv.validate();
System.out.println(pv.getResponse());
System.out.println();
if (pv.isAuthenticationSuccesful()) {
user = pv.getUser();
System.out.println("user: " + pv.getUser());
System.out.println("proxies:\n " + pv.getProxyList());
} else {
System.out.println("error code: " + pv.getErrorCode());
System.out.println("error message: " + pv.getErrorMessage()); } } catch (Exception e) { System.out.println("error : " + e); } return "(user : "+user+")"; } public String hm() { numberOfCalls++; SimpleDateFormat tmp = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss"); String tmp2 = "; (Session : "+ numberOfCalls+"); (user : "+user+")"; return tmp.format(new Date()) + tmp2; } }
Explications :
Voici le WSDD utilisé pour déployer ce Web Service dans AXIS :
<deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <service name="WSTest2" provider="java:RPC"> <parameter name="scope" value="session"/> <parameter name="className" value="org.esupportail.WSTest2"/> <parameter name="allowedMethods" value="*"/> </service> </deployment>
Explications :
Le fichier WSDD est pris en compte par AXIS grâce à l'utilisation de l'utilitaire AXIS AdminClient. Exemple d'un fichier ant appelant cet utilitaire :
<target name="maj wsdd"> <echo message="Appel de AdminClient"/> <property name="wsdd" value=" "/> <java fork="true" dir="${basedir}" classname="org.apache.axis.client.AdminClient"> <classpath> <pathelement path="${axis.lib}/axis.jar"/> <pathelement path="${axis.lib}/commons-discovery.jar"/> <pathelement path="${axis.lib}/commons-logging.jar"/> <pathelement path="${axis.lib}/jaxrpc.jar"/> <pathelement path="${axis.lib}/saaj.jar"/> <pathelement path="${axis.lib}/log4j-1.2.8.jar"/> <pathelement path="${axis.lib}/xml-apis.jar"/> <pathelement path="${axis.lib}/xercesImpl.jar"/> </classpath> <arg value="${wsdd}"/> </java> </target>
Nous devons maintenant modifier le canal présenté au paragraphe 2 afin d'atteindre les objectifs suivants :
La solution la plus élégante pour écrire un client de service Web avec Axis consiste à générer du code avec l'utilitaire WSDL2Java livré avec Axis.
On peut consulter le WSDL décrivant un service Web déployé dans un conteneur Axis, simplement en référençant l'URL de ce service Web et en y ajoutant "?wsdl". Typiquement dans notre cas : http://localhost:8080/axis/services/WSTest2?wsdl
Il est à noter que le développeur n'a pas à écrire le WSDL, il est automatiquement généré par le conteneur Axis.
On utilise alors tout simplement WSDL2Java en lui passant en paramètre l'url du WSDL. Cet utilitaire va générer un ensemble de classes java regroupées dans un package "localhost". Si l'on avait référencé un WSDL d'un service Web sur un site ayant une adresse de type site.domaine.com on aurait obtenu un package de nom "com.domaine.site".
WSDL2Java génère le code dans un répertoire temporaire. Il convient de déplacer ce code dans le classpath courant.
La première chose à faire consiste à importer le package généré par l'utilitaire WSDL2Java en ajoutant au début du code du canal :
import localhost.*;
On déclare ensuite des objets de type WSTest2Service et WSTest2 :
private WSTest2Service service; private WSTest2 ws;
On initialise ces deux objets dans le constructeur :
service = new WSTest2ServiceLocator(); ws = service.getWSTest2();
On modifie ensuite le code de la méthode setRuntimeData pour faire appel au service Web :
public void setRuntimeData(ChannelRuntimeData rd) { this.runtimeData = rd; if (runtimeData.getParameter("appel") != null) { //appel au service Web try { retWS = ws.hm(); } catch (Exception e) { System.out.println(e); } } }Comme on peut le constater l'appel au service Web est des plus simple - ws.hm(). Le code généré par WSDL2Java masque complètement la complexité liée aux services Web.
L'obtention d'un PT dans un canal est aussi une chose simple grâce à l'utilisation de la bibliothèque ESUP org.esup.utils.CAS et sa méthode getPT() (Cf. Librairie CAS Pour uPortal).
Typiquement, on ne doit chercher à obtenir le PT qu'une fois. La méthode setStaticData() de l'interface IChanel est donc une bonne candidate pour cela. C'est aussi là que l'on va faire appel à la méthode login() de notre Web service pour initialiser notre session.
Voici le code de la méthode setStaticData() :
public void setStaticData(ChannelStaticData sd) { this.staticData = sd; try { PT = CAS.get_pt(sd, "http://localhost:8080/axis/services/WSTest2"); ((WSTest2ServiceLocator)service).setMaintainSession(true); retWS = ws.login(PT); } catch (Exception e) { e.printStackTrace(); } }
La encore, c'est assez simple, vous l'avez peut-être même déjà remarqué dans le code de la méthode setStaticData(). Il suffit de "dire" à l'objet service de maintenir la session avec un code du type :
((WSTest2ServiceLocator)service).setMaintainSession(true);
On doit retrouver ce code de maintien de session, en plus de la méthode setStaticData() qui fait appel à la méthode login() de notre Web Service, dans la méthode setRuntimeData() qui fait appel à la méthode hm() de notre Web Service. Ce qui donne :
public void setRuntimeData(ChannelRuntimeData rd) { // process the form submissions this.runtimeData = rd; if (runtimeData.getParameter("appel") != null) { //appel au service Web try { ((WSTest2ServiceLocator)service).setMaintainSession(true); retWS = ws.hm(); } catch (Exception e) { System.out.println(e); } } }
Au final, avec tous les textes de trace ajoutés dans le canal et le Web Service voici ce que donne l'utilisation de canal :
[1] Owen Gunden, Writing a channel for uPortal 2.0.1, http://www.stwing.upenn.edu/~ogunden/uportal_2-0-1/channel_2-0-1.html : Juin 2002
[2] Format XML propre à AXIS pour décrire un Web Service avant de le déployer
Création : 1 Décembre- Raymond Bourges (Université de Rennes 1) | |
Modifications :
|