IFT604_6f_SOAP_J2ME_exemple

publicité
XML, SOAP, Services Web et
JAVA Me
SOAP Web Services on
Smart Clients
Plan
 Services Web
XML over http
SOAP - RPC
Avantages de SOAP
 Support pour le typage
 Plus de 40 types de données avec les schémas XML
 Permet aux usagers de se définir des types de données complexes
 Messagerie flexible et répandue
 Supporte plusieurs schémas de messagerie dont




RPC synchrone,
messages asynchrones,
multicast (souscription),
routage complexe des messages avec plusieurs intermédiaires
 Standardisation
 Adhérence répandue à SOAP par les services web
 Profiter des standards XML qui peuvent être intégrés à SOAP ou utiliser SOAP
 WSDL (Web Services Description Language), UDDI (Universal Description, Discovery, and
Integration), et la plupart des registres XML;
 XML Digital Signature, XML Encryption, SAML (Security Assertion Markup Language)
 Rem: sérialisation d’objets Java
 En vecteur d’octets en tant qu’élément Base64 pour les transporter par SOAP
 Par contre, si on sait que tout se passe en Java, il vaut mieux utiliser RMI ou JMS
Architecture des services web SOAP
 J2ME Web Services API (WSA)
 Pour tout profil J2ME CDC ou CLDC
 remote invocation API
Sous-ensemble strict de J2SE Java API for XML-Based RPC (JAX-RPC 1.1),
+ quelques classes Remote Method Invocation (RMI) classes ajoutées pour
satisfaire les dépendances de JAX-RPC
Un sous-ensemble strict de SAX, version 2
Organisation d’une application
fondée sur JSR
JSR172 run-time et stubs
 runtime
Permet aux stubs de réaliser toutes les tâches associées à
l’invocation d’un RPC à service web
Déterminer les propriétés spécifiques à une invocation RPC
Décrire les valeurs d’entrée et le résultat d’une invocation RPC
Encoder les valeurs d’entrée
Invoquer le RPC au point de livraison du service web
Décoder et rendre à l’application tout résultat rendu par le service
kSOAP
Construit au-dessus de l’analyseur kXML
http://ksoap2.sourceforge.net/
Empreinte petite : 41 K pour v2.1.2
Ne supporte pas la spécification entière de SOAP
L’exemple qui suit est réalisé avec kSOAP v1.2
Analyseur SOAP
 Extraire directement les objets Java d’un document
SOAP
Type-mapping
Mashalling via un encodage sous forme de chaîne de caractères
Comprends les informations de typage dans un document SOAP
Convertit automatiquement les éléments SOAP en objets Java
 Serialisation / désérialisation devient transparente
Exemple simple
 Préparer les arguments à transmettre à la méthode
distante
Instancier un objet SOAP (SoapObject)
Ajouter les arguments de l’appel addProperty()
 Préparer le transport
 objet HttpTransport + URL destination
 Invoquer la méthode distante
Invocation de la méthode call() sur l’objet HttpTransport
Le résultat est rendu par cette invocation
private String licenseKey = "fVqHySRrXuf";
private String endPointURL ="http://api.google.com/search/beta2";
/**
* Simple spell check using convenience class HttpTransport
* This function returns the Google suggested spell.
* @param query the string to be spell corrected.
*/
public String spellCheck (String query) throws Exception {
// Prepare request SOAP message in a memory object
SoapObject method = new SoapObject("urn:GoogleSearch",
"doSpellingSuggestion");
method.addProperty("key", licenseKey);
method.addProperty("phrase", query);
// Prepare SOAP RPC call object.
HttpTransport rpc = new HttpTransport(endPointURL);
// Google uses 1999 SOAP standards.
ClassMap cm = new ClassMap(Soap.VER10);
rpc.setClassMap (cm);
// Conduct RPC call through HTTP and directly get results
String spellSugg = (String) rpc.call (method);
System.out.println(rpc.requestDump);
System.out.println(rpc.responseDump);
return spellSugg;
}
Fonctionnement de la méthode
‘call’
1- Sérialisation de l’objet SoapObject sous forme de message de requête avec les bons espaces de
noms
<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:doSpellingSuggestion
xmlns:ns1="urn:GoogleSearch"
SOAP-ENV:encodingStyle= "http://schemas.xmlsoap.org/soap/encoding/">
<key xsi:type="xsd:string">QLgFCbGCQD6710PlS</key>
<phrase xsi:type="xsd:string">phon</phrase>
</ns1:doSpellingSuggestion>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Fonctionnement de la méthode
‘call’
 2. soumission de la requête au point de livraison à travers HTTP
 3. récupération du message de réponse SOAP
<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:doSpellingSuggestionResponse
xmlns:ns1="urn:GoogleSearch"
SOAP-ENV:encodingStyle= "http://schemas.xmlsoap.org/soap/encoding/">
<return xsi:type="xsd:string">phone</return>
</ns1:doSpellingSuggestionResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
 4. transformer la réponse en objet Java et rendre cet objet
 La chaîne « phone » de type String
Transport des message en
kSOAP
package org.ksoap.transport;
import java.io.*;
import javax.microedition.io.*;
import org.kxml.*;
import org.kxml.io.*;
import org.kxml.parser.*;
import org.ksoap.*;
public class HttpTransport {
String url;
String soapAction = "\"\"";
SoapEnvelope requestEnvelope = new SoapEnvelope ();
SoapEnvelope responseEnvelope = new SoapEnvelope ();
HttpConnection connection;
OutputStream os;
InputStream is;
InputStreamReader reader;
Transport des message en
kSOAP
/** Sends the requestEnvelope and fills the responseEnvelope
@exception InterruptedIOException if transport was closed async.
@exception IOException if an error occurs
*/
public void call () throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream ();
XmlWriter xw = new XmlWriter (new OutputStreamWriter (bos));
requestEnvelope.write (xw);
xw.flush ();
bos.write ('\r');
bos.write ('\n');
byte [] requestData = bos.toByteArray ();
bos = null;
xw = null;
requestDump = debug ? new String (requestData) : null;
responseDump = null;
try {
connected = true;
connection =
(HttpConnection) Connector.open (url, Connector.READ_WRITE, true);
connection.setRequestProperty ("SOAPAction", soapAction);
connection.setRequestProperty ("Content-Type", "text/xml");
connection.setRequestProperty ("Content-Length", ""+requestData.length);
connection.setRequestProperty ("User-Agent", "kSOAP/1.0");
connection.setRequestMethod (HttpConnection.POST);
os = connection.openOutputStream ();
os.write (requestData, 0, requestData.length);
os.close ();
requestData = null;
is = connection.openInputStream ();
reader = new InputStreamReader (is);
XmlParser xp = new XmlParser (reader);
responseEnvelope.parse (xp);
} finally {
if (!connected) throw new InterruptedIOException ();
reset ();
}
}
Génération des stubs
IBM WebSphere Studio Device Developer
SunOne Studio
CodeWarrior Wireless Studio
Fonctionnement interne
de kSOAP v 1.2
 Lorsque l’analyseur kSOAP rencontre un élément SOAP, l’analyseur lit l’élément XML
dans un objet Java selon les règles suivantes:
 Si l’élément SOAP est un des type primitifs prédéfini du tableau, il est converti dans un objet
Java du type correspondant
 Si l’élément SOAP n’a pas de fils, mais n’est pas un des types prédéfini, il est converti en un
objet SoapPrimitive
 On peut récupérer les informations de typage de l’élément originel SOAP via les méthodes
SoapPrimitive.getNameSpace() et SoapPrimitive.getName()
 On peut accéder la valeur de la chaîne de caractères via la méthode SoapPrimitive.toString()
 Si l’élément SOAP a des fils, i.e. c’est un élément complexe, il est converti en objet
implémentant l’interface KvmSerializable
 La classe utilitaire SoapObject implémente cet interface
 On peut récupérer les information s typages via les méthodes SoapObject.getNameSpace() et
SoapObject.getName()
 Les fils d’un élément complexe sont convertis en propriétés de leur parent SoapObject selon
les règles précédentes.
 Chaque propriété possède se voit aussi associé un objet PropertyInfo qui contient les informations
comme le nom de l’élément SOAP et son type Java
Structure des SoapObject
 Parce qu’un objet SoapObject peut avoir des propriétés SoapObject, on
peut l’utiliséer pour représenter des structures hiérarchiques complexes
Sérialiser un objet
 Pour sérialiser un objet
 Construire une représentaiton hiérarchique où
Toutes les feuilles doivent être soit un objet SoapPrimitive, soit un des
quatre types prédéfinis
La racine par contre ne possède pas de paires (SoapObject, PropertyInfo),
on a donc perdu en principe le nom de la racine lors de l’analyse
 Utiliser un objet « writer » kSOAP pour sérialiser l’objet en mémoire dans un flot
XML
// SoapObject "method" is the calling construct //
// the "" and "StockOrderParameters" here are
// element name/namespace rather than SOAP type name/namespace
SoapObject method = new SoapObject("", "StockOrderParameters");
method.addProperty("Symbol", "XYZ");
method.addProperty("From", "Michael Yuan");
method.addProperty("Shares", new Integer (1000));
method.addProperty("Buy", new Boolean (true));
method.addProperty("LimitPrice", new SoapPrimitive
("http://www.w3.org/2001/XMLSchema", "float", "123.4"));
// Assemble "method" into an enveloped SOAP message
// and then export to a String
ByteArrayOutputStream bos = new ByteArrayOutputStream ();
XmlWriter xw = new XmlWriter (new OutputStreamWriter (bos));
// Use default mapping between Java objects and Soap elements
SoapEnvelope envelope = new SoapEnvelope (new ClassMap (Soap.VER12));
envelope.setBody (method);
envelope.write (xw);
xw.flush ();
bos.write ('\r');
bos.write ('\n');
byte [] requestData = bos.toByteArray ();
String requestSOAPmesg = new String (requestData);
Requête
<SOAP-ENV:Envelope
xmlns:SOAP-ENC="http://www.w3.org/2001/12/soap-encoding"
xmlns:SOAP-ENV="http://www.w3.org/2001/12/soap-envelope"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<SOAP-ENV:Body
SOAP-ENV:encodingStyle="http://www.w3.org/2001/12/soapencoding">
<StockOrderParameters id="o0" SOAP-ENC:root="1">
<Symbol xsi:type="xsd:string">XYZ</Symbol>
<From xsi:type="xsd:string">Michael Yuan</From>
<Shares xsi:type="xsd:int">1000</Shares>
<Buy xsi:type="xsd:boolean">true</Buy>
<LimitPrice xsi:type="xsd:float">123.45</LimitPrice>
</StockOrderParameters>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Réponse
Récupérer les éléments de la réponse
ByteArrayInputStream bis = new ByteArrayInputStream (soapRespMesg.getBytes ());
InputStreamReader reader = new InputStreamReader (bis);
XmlParser xp = new XmlParser (reader);
// Use default mapping between Java objects and Soap elements.
SoapEnvelope envelope = new SoapEnvelope (new ClassMap (Soap.VER12));
envelope.parse (xp);
// Get the parsed structure.
SoapObject orderStatus = (SoapObject) envelope.getResult();
// Retrieve the values as appropriate Java objects.
String customerName = (String) orderStatus.getProperty ("CustomerName");
String symbol = (String) orderStatus.getProperty ("Symbol");
Integer share = (Integer) orderStatus.getProperty ("Share");
Boolean buy = (Boolean) orderStatus.getProperty ("Buy");
// Since the default mapping table has no "Float" type, there is no corresponding
// Java object type for "xsd:float". So, as any unknown type,
// this element is mapped to a "SoapPrimitive".
SoapPrimitive price = (SoapPrimitive) orderStatus.getProperty ("Price");
SoapPrimitive execTime = (SoapPrimitive) orderStatus.getProperty ("ExecTime");
Automatiser la sérialisation /
désérialisation
 Indiquer à l’analyseur la relation entre les type SOAP et les types
Java
 Compléter la table de base
 Indiquer à l’analyseur comment convertir les chaînes de caractères
en objet Java
 Enregistrer dans la table un objet Marshal qui sait faire la conversion
public interface Marshal {
public Object readInstance (
SoapParser parser,
String namespace,
String name,
ElementType expected)
throws IOException;
public void writeInstance (SoapWriter writer, Object instance)
throws IOException;
public void register (ClassMap cm);
}
La classe utilitaire
org.ksoap.marshal.MarshalDate
Public class MarshalDate implements Marshal
public Object readInstance (
SoapParser parser,
String namespace,
String name,
ElementType expected)
throws IOException {
parser.parser.read ();
// Start tag
Object result = IsoDate.stringToDate (
parser.parser.readText (),
IsoDate.DATE_TIME);
parser.parser.read ();
// End tag
return result;
}
public void register (ClassMap cm) {
cm.addMapping (cm.xsd, "dateTime", MarshalDate.DATE_CLASS, this);
}
ClassMap.addMapping
/** Defines a direct mapping from a namespace and name to a Java class
(and vice versa), using the given marshal mechanism
*/
public void addMapping (String namespace, String name, Class clazz, Marshal
marshal)
{
qNameToClass.put (
new SoapPrimitive (namespace, name, null),
marshal == null ? (Object) clazz : marshal);
classToQName.put (
clazz.getName (),
new Object [] {namespace, name, null, marshal});
if (prefixMap.getPrefix (namespace) == null)
prefixMap = new PrefixMap (prefixMap, "n"+(cnt++), namespace);
}
Ajout à la table des mappings
avant de désérialiser la réponse
ByteArrayInputStream bis = new ByteArrayInputStream
(mesg.getBytes ());
InputStreamReader reader = new InputStreamReader (bis);
XmlParser xp = new XmlParser (reader);
// Register Marshal for "xsd:dateTime" type
ClassMap cm = new ClassMap (Soap.VER12);
Marshal md = new MarshalDate ();
md.register (cm);
SoapEnvelope envelope = new SoapEnvelope (cm);
envelope.parse (xp);
SoapObject orderStatus =(SoapObject)envelope.getResult();
Date execTime = (Date) orderStatus.getProperty ("ExecTime");
La sérialisation des « array »
La désérialisation sous forme de Vector
// Register Marshal for "xsd:dateTime" type
ClassMap cm = new ClassMap (Soap.VER12);
Marshal md = new MarshalDate ();
md.register (cm);
SoapEnvelope envelope = new SoapEnvelope (cm);
envelope.parse (xp);
SoapObject orderStatus = (SoapObject) envelope.getResult();
String customerName = (String) orderStatus.getProperty ("CustomerName");
Vector transactions = (Vector) orderStatus.getProperty ("Transactions");
// First element in the array
SoapObject transaction0 = (SoapObject) transactions.elementAt(0);
String symbol0 = (String) transaction0.getProperty ("Symbol");
Integer share0 = (Integer) transaction0.getProperty ("Share");
Boolean buy0 = (Boolean) transaction0.getProperty ("Buy");
SoapPrimitive price0 = (SoapPrimitive) transaction0.getProperty ("Price");
// Second element in the array SoapObject transaction1 = (SoapObject)
transactions.elementAt(1);
String symbol1 = (String) transaction1.getProperty ("Symbol");
Integer share1 = (Integer) transaction1.getProperty ("Share");
Boolean buy1 = (Boolean) transaction1.getProperty ("Buy");
SoapPrimitive price1 = (SoapPrimitive) transaction1.getProperty ("Price");
Validation des documents à
l’aide des templates SOAP
 Via la classe ClassMap
Ajouter un objet template SoapObject à l’analyseur
 Template
un SoapObject vide avec de l’information sur
le type SOAP parent
Les noms des éléments des enfants (properties)
Leur type Java
Un fils peut lui-même être un template ce qui permet de
construire des templates arbitrairement complexes
 Si l’analyseur détecte une erreur, alors il lance une
exception et s’arrête
Une transaction doit contenir
xsd:string value Symbol,
xsd:int value Share,
xsd:boolean value Buy,
xsd:float value Price
ByteArrayInputStream bis = new ByteArrayInputStream
(arraySoapRespMesg.getBytes ());
InputStreamReader reader = new InputStreamReader (bis);
XmlParser xp = new XmlParser (reader);
ClassMap cm = new ClassMap (Soap.VER12);
// Register Marshal for "xsd:dateTime" type
Marshal md = new MarshalDate ();
md.register (cm);
SoapObject so = new SoapObject
("http://www.javaworld.com/ksoap/test", "transaction");
so.addProperty ("Symbol", new String (""));
so.addProperty ("Share", new Integer (0));
so.addProperty ("Buy", new Boolean (true));
so.addProperty ("Price", new SoapPrimitive ("xsd", "float", ""));
cm.addTemplate (so);
SoapEnvelope envelope = new SoapEnvelope (cm);
envelope.parse (xp);
SoapObject orderStatus = (SoapObject) envelope.getResult();
Comparaison entre le package J2ME Web
Services Optional Package et kXML + kSOAP
Modèle de traitement XML
 J2ME Web Services Optional Package
SAX seulement
kXML
SAX, XMLPull et kDOM
Centré Java ou centré XML
 J2ME Web Services Optional Package
Traite SOAP RPC de manière similaire à RMI RPC
Pas de contrôle sur les messages
kXML
Plus de prise sur la structure XML interne
Références

Enterprise J2ME
 Chapitre 16

Access Web services from wireless devices, Handle SOAP messages on MIDP devices using kSOAP
 http://www.javaworld.com/javaworld/jw-08-2002/jw-0823-wireless.html?page=1
Téléchargement