350 likes | 567 Views
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
E N D
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
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/soap-encoding"> <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é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 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