Remote Webservice
From Tera-WURFL
(→ActionScript) |
(→JavaScript) |
||
Line 149: | Line 149: | ||
===JavaScript=== | ===JavaScript=== | ||
<pre> | <pre> | ||
- | + | /** | |
- | + | * Tera-WURFL remote webservice client for JavaScript | |
- | function | + | * |
- | + | * Tera-WURFL was written by Steve Kamerman, and is based on the | |
- | + | * Java WURFL Evolution package by Luca Passani and WURFL PHP Tools by Andrea Trassati. | |
- | + | * This version uses a MySQL database to store the entire WURFL file, multiple patch | |
+ | * files, and a persistent caching mechanism to provide extreme performance increases. | ||
+ | * | ||
+ | * @author Steve Kamerman <stevekamerman AT gmail.com> | ||
+ | * @version Stable 2.1.1 (2010/02/21 17:41:47) | ||
+ | * @license http://www.mozilla.org/MPL/ MPL Vesion 1.1 | ||
+ | * | ||
+ | * Documentation is available at http://www.tera-wurfl.com | ||
+ | */ | ||
+ | function TeraWurflRemoteClient(webservice){ | ||
+ | // Properties | ||
+ | this.webservice = webservice; | ||
+ | this.capabilities = new Array(); | ||
+ | this.errors = new Array(); | ||
+ | this.xmlHttpReq = false; | ||
var self = this; | var self = this; | ||
- | fullURL = | + | |
- | + | // Methods | |
- | + | this.getCapabilitiesFromAgent = function(userAgent,capabilities_search){ | |
- | + | this.userAgent = userAgent; | |
- | + | this.search = capabilities_search.join('|'); | |
- | + | this.fullURL = this.webservice + '?ua=' + this.urlencode(this.userAgent) + '&search=' + this.search; | |
- | + | ||
- | + | if(window.XMLHttpRequest) { | |
- | + | self.xmlHttpReq = new XMLHttpRequest(); | |
- | + | }else if (window.ActiveXObject) { | |
- | + | try{ | |
+ | self.xmlHttpReq = new ActiveXObject("Msxml2.XMLHTTP"); | ||
+ | }catch(e){ | ||
+ | try{ | ||
+ | self.xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP"); | ||
+ | }catch(e){} | ||
+ | } | ||
} | } | ||
+ | self.xmlHttpReq.open('GET', this.fullURL, true); | ||
+ | self.xmlHttpReq.onreadystatechange = this.handleResponse; | ||
+ | self.xmlHttpReq.send(null); | ||
} | } | ||
- | |||
- | |||
- | function | + | this.handleResponse = function(){ |
- | + | if(self.xmlHttpReq.readyState != 4) return; | |
- | + | if(self.xmlHttpReq.status == 12029 || self.xmlHttpReq.status == 12007){ | |
- | + | alert("Error: Could not connect to remote webservice"); | |
- | + | return; | |
- | + | } | |
- | + | if (window.ActiveXObject) { // for IE | |
- | + | var doc = self.xmlHttpReq.responseXML; | |
- | + | }else{ // code for Mozilla, Firefox, Opera, etc. | |
- | + | var parser=new DOMParser(); | |
+ | var doc=parser.parseFromString(self.xmlHttpReq.responseText,"text/xml"); | ||
+ | } | ||
+ | try{ | ||
+ | var data = ""; | ||
+ | if(doc.documentElement){ //Response from webservice | ||
+ | data = doc.documentElement; | ||
+ | self.receivedRemoteCapabilities(data); | ||
+ | }else{ | ||
+ | data = self.xmlHttpReq.responseText; //from web page | ||
+ | alert("No XML:\nresponseText["+data+"]\nresponseXML["+self.xmlHttpReq.responseXML+"]\nAll Headers:\n["+self.xmlHttpReq.getAllResponseHeaders()+"]"); | ||
+ | } | ||
+ | }catch(e) {} | ||
+ | } | ||
+ | |||
+ | this.receivedRemoteCapabilities = function(xml_response){ | ||
+ | this.devices = xml_response.getElementsByTagName('device'); | ||
+ | this.capabilitiesXML = xml_response.getElementsByTagName('capability'); | ||
+ | this.errorsXML = xml_response.getElementsByTagName('error'); | ||
+ | var name, value, i; | ||
+ | // Put capabilities into an object / associative array | ||
+ | for(i=0;i<this.capabilitiesXML.length;i++){ | ||
+ | name = this.capabilitiesXML[i].getAttribute('name'); | ||
+ | value = this.capabilitiesXML[i].getAttribute('value'); | ||
+ | this.capabilities[name] = value; | ||
+ | } | ||
+ | // Put errors into an array | ||
+ | for(i=0;i<this.errorsXML.length;i++){ | ||
+ | errname = this.errorsXML[i].getAttribute('name'); | ||
+ | errdesc = this.errorsXML[i].getAttribute('description'); | ||
+ | this.errors[i] = {name: errname, description: errdesc}; | ||
+ | } | ||
+ | this.onUpdate(this.capabilities,this.errors); | ||
+ | } | ||
+ | |||
+ | this.urlencode = function(str){ | ||
+ | str = (str+'').toString(); | ||
+ | return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+'); | ||
} | } | ||
} | } | ||
</pre> | </pre> |
Revision as of 22:22, 28 February 2010
Contents |
Introduction
Tera-WURFL 2.1.1 introduced a full-featured webservice and remote client for many languages. Some reasons to use the remote webservice to query Tera-WURFL are:
- You want to share your Tera-WURFL database over multiple installations or servers, so you just install Tera-WURFL once in the backend and use the remote client on all the frontend servers (you only need one file for the remote client).
- You want to query Tera-WURFL from a language other than PHP, like JavaScript, ActionScript 3, Perl, Python, VBScript, .NET, ruby, etc... Any language that can retrieve a remote file and parse it as XML can use the webservice.
- You want to provide managed mobile device detection capabilities to your customers without giving them access to the Tera-WURFL library itself.
Backend Files
TeraWurflWebservice.php
This file defines the TeraWurflWebservice class and provides the functionality to parse an incoming request, query Tera-WURFL and return the results in XML form.
webservice.php
The webservice.php file takes the raw request from the remote client and decodes it if necessary, then passes it to the TeraWurflWebService class for processing. The file then sends the response back to the client that requested it.
Frontend Files
PHP
For PHP, there is a full-blown client API that mimics the standalone API called TeraWurflRemoteClient. The remote client (available from the TeraWurflRemoteClient/ folder) can be moved to any location, even a different server, and used to query your Tera-WURFL installation. Just include TeraWurflRemoteClient.php in your scripts and use it as follows:
require_once('../TeraWurflRemoteClient.php'); $wurflObj = new TeraWurflRemoteClient('http://localhost/Tera-Wurfl/webservice.php'); // The groups or capabilities you want to use $capabilities = array("is_wireless_device|brand_name|model_name|playback|tera_wurfl"); $wurflObj->getCapabilitiesFromAgent(TeraWurflRemoteClient::getUserAgent(),$capabilities); echo "You are on a {$wurflObj->getDeviceCapability('brand_name')} {$wurflObj->getDeviceCapability('model_name')}<br/>"; $text = $wurflObj->getDeviceCapability('is_wireless_device')? "This device is wireless": "This device is a desktop web browser"; echo $text;
Perl
The Perl client uses URI, LWP::Simple and XML::Simple to request the webservice and process the response. Although I've been programming in Perl since 1996, I haven't used it much for web applications or XML. If there is a more efficient, readable way to do this, please let me know!
#!/usr/bin/perl use strict; use URI; use LWP::Simple; use XML::Simple; # Location of Tera-WURFL webservice my $webservice = URI->new("http://localhost/Tera-Wurfl/webservice.php"); # The User Agent you would like to check my $user_agent = "Mozilla/5.0 (Linux; U; Android 1.0; en-us; dream) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2"; # Capabilities and Groups you want to find my $search = "brand_name|model_name|marketing_name|is_wireless_device|device_claims_web_support|tera_wurfl"; # Build the query String $webservice->query_form( "ua" => $user_agent, "search" => $search ); # Make webservice request my $xml_response = get $webservice; # Parse webserver response my $xml_parser = new XML::Simple(forcearray => 1, keyattr => ['key']); my $xml_object = $xml_parser->XMLin($xml_response); # Convert XML Object into Perl Hash my %capabilities; foreach(@{$xml_object->{device}[0]->{capability}}){ $capabilities{$_->{name}}=$_->{value}; } # Make top-level properties available in hash my %properties = ( "apiVersion", $xml_object->{device}[0]->{apiVersion}, "id", $xml_object->{device}[0]->{id}, "user_agent", $xml_object->{device}[0]->{useragent} ); # Tera-WURFL proccessing is finished, capabilities are available in %capabilities, properties in %properties print "-- Response from Tera-WURFL $properties{apiVersion}\n"; print "-- Device Detected as: $capabilities{brand_name} $capabilities{model_name} $capabilities{marketing_name}\n"; my($name,$value); while(($name,$value) = each(%capabilities)){ print "$name: $value\n"; }
Python
I don't use Python, but I learned enough of it to make this client/example. If one of you Python gurus want to clean it up for me and explain your changes, please send it my way and I will be glad to include it in a future release.
# -*- coding: utf-8 -*- # Python from urllib import quote from urllib import urlopen from xml.dom.minidom import parseString # Location of Tera-WURFL webservice webservice = "http://localhost/Tera-Wurfl/webservice.php" # The User Agent you would like to check user_agent = "Mozilla/5.0 (Linux; U; Android 1.0; en-us; dream) AppleWebKit/525.10+ (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2" # Capabilities and Groups you want to find search = "brand_name|model_name|marketing_name|is_wireless_device|device_claims_web_support|tera_wurfl" querystring = "?ua=" + quote(user_agent) + "&search=" + search xml_response = urlopen(webservice + querystring).read() xml_object = parseString(xml_response) deviceNode = xml_object.firstChild.childNodes[1] errorsNode = xml_object.firstChild.childNodes[3] capabilitiesNodes = deviceNode.getElementsByTagName("capability"); # Setup Top Level Properties properties = { "apiVersion": deviceNode.attributes['apiVersion'].value, "id": deviceNode.attributes['id'].value, "useragent": deviceNode.attributes['useragent'].value } # Setup Capabilities capabilities = {} for capNode in capabilitiesNodes: # print capNode.toxml() + capNode.attributes['name'].value + capNode.attributes['value'].value + "\n" capabilities[capNode.attributes['name'].value] = capNode.attributes['value'].value # Tera-WURFL processing is finished, properties and capabilities dictionaries are now filled with data print "Response from Tera-WURFL " + properties['apiVersion']; for name, value in capabilities.items(): print name + ": " + value
ActionScript
The complete usage example with a Flash FLA is included in the distribution under TeraWurflRemoteClient/examples/ActionScript. I've done a lot of work in AS1, AS2, AS3, Flash, and Flex so I plan to develop a full-featured client like the PHP TeraWurflRemoteClient. Now I just need to figure out what people would really need an ActionScript Tera-WURFL client for! If you have a use for this client, please let me know what you use it for!
btnDetect.addEventListener(MouseEvent.CLICK,startDetection); function startDetection(event:Event):void { var xml:XML; var urlRequest:URLRequest = new URLRequest("http://localhost/Tera-Wurfl/webservice.php?ua=" + escape(txtUA.text) + "&search=" + txtCapabilities.text); var urlLoader:URLLoader = new URLLoader(); urlLoader.addEventListener(Event.COMPLETE, urlLoader_complete); urlLoader.load(urlRequest); } function urlLoader_complete(evt:Event):void { txtResult.text = 'Result:\n'; var xml = new XML(evt.currentTarget.data); for each( var i:Object in xml..capability){ txtResult.appendText(i.@name + ": " + i.@value + "\n"); } }
JavaScript
/** * Tera-WURFL remote webservice client for JavaScript * * Tera-WURFL was written by Steve Kamerman, and is based on the * Java WURFL Evolution package by Luca Passani and WURFL PHP Tools by Andrea Trassati. * This version uses a MySQL database to store the entire WURFL file, multiple patch * files, and a persistent caching mechanism to provide extreme performance increases. * * @author Steve Kamerman <stevekamerman AT gmail.com> * @version Stable 2.1.1 (2010/02/21 17:41:47) * @license http://www.mozilla.org/MPL/ MPL Vesion 1.1 * * Documentation is available at http://www.tera-wurfl.com */ function TeraWurflRemoteClient(webservice){ // Properties this.webservice = webservice; this.capabilities = new Array(); this.errors = new Array(); this.xmlHttpReq = false; var self = this; // Methods this.getCapabilitiesFromAgent = function(userAgent,capabilities_search){ this.userAgent = userAgent; this.search = capabilities_search.join('|'); this.fullURL = this.webservice + '?ua=' + this.urlencode(this.userAgent) + '&search=' + this.search; if(window.XMLHttpRequest) { self.xmlHttpReq = new XMLHttpRequest(); }else if (window.ActiveXObject) { try{ self.xmlHttpReq = new ActiveXObject("Msxml2.XMLHTTP"); }catch(e){ try{ self.xmlHttpReq = new ActiveXObject("Microsoft.XMLHTTP"); }catch(e){} } } self.xmlHttpReq.open('GET', this.fullURL, true); self.xmlHttpReq.onreadystatechange = this.handleResponse; self.xmlHttpReq.send(null); } this.handleResponse = function(){ if(self.xmlHttpReq.readyState != 4) return; if(self.xmlHttpReq.status == 12029 || self.xmlHttpReq.status == 12007){ alert("Error: Could not connect to remote webservice"); return; } if (window.ActiveXObject) { // for IE var doc = self.xmlHttpReq.responseXML; }else{ // code for Mozilla, Firefox, Opera, etc. var parser=new DOMParser(); var doc=parser.parseFromString(self.xmlHttpReq.responseText,"text/xml"); } try{ var data = ""; if(doc.documentElement){ //Response from webservice data = doc.documentElement; self.receivedRemoteCapabilities(data); }else{ data = self.xmlHttpReq.responseText; //from web page alert("No XML:\nresponseText["+data+"]\nresponseXML["+self.xmlHttpReq.responseXML+"]\nAll Headers:\n["+self.xmlHttpReq.getAllResponseHeaders()+"]"); } }catch(e) {} } this.receivedRemoteCapabilities = function(xml_response){ this.devices = xml_response.getElementsByTagName('device'); this.capabilitiesXML = xml_response.getElementsByTagName('capability'); this.errorsXML = xml_response.getElementsByTagName('error'); var name, value, i; // Put capabilities into an object / associative array for(i=0;i<this.capabilitiesXML.length;i++){ name = this.capabilitiesXML[i].getAttribute('name'); value = this.capabilitiesXML[i].getAttribute('value'); this.capabilities[name] = value; } // Put errors into an array for(i=0;i<this.errorsXML.length;i++){ errname = this.errorsXML[i].getAttribute('name'); errdesc = this.errorsXML[i].getAttribute('description'); this.errors[i] = {name: errname, description: errdesc}; } this.onUpdate(this.capabilities,this.errors); } this.urlencode = function(str){ str = (str+'').toString(); return encodeURIComponent(str).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+'); } }