w eb s erv ices a n d s o a p - university of california ... · w eb s erv ices a n d s o a p ......
TRANSCRIPT
Web Services and SOAPMark Slater
CMPS 183 - Hypermedia and the WebMay 19, 2005
Department of Computer ScienceBaskin School of Engineering
UC Santa Cruz
Web Services
Programming Web Services with SOAP,James Snell et. al., O’Reilly, January 2002
“... a network accessible interface to application functionality, built using standard Internet technology”
Web-based abstraction layer between application client and application code
Web Services in History
CORBA - Common Object Request Broker Architecture
Platform and language independent object-oriented architecture
JAVA - Remote Method Invocation
JavaScript - 1996 by Netscape
Flash - 1996 by Macromedia
Modern Web Services
XML-RPC
Remote Procedure Call using XML messages in the body of an HTTP POST.
Invented by Dave Winer of Userland software
Non-standard
Example XML-RPCPOST /RPC2 HTTP/1.0User-Agent: Frontier/5.1.2 (WinNT)Host: betty.userland.comContent-Type: text/xmlContent-length: 181
<?xml version="1.0"?><methodCall> <methodName>examples.getStateName</methodName> <params> <param> <value><i4>41</i4></value> </param> </params></methodCall>
Source: http://www.xmlrpc.org/spec
XML-RPC
Types: int (i4), boolean, string, double, dateTime, base64, struct
Example Struct:<struct> <member> <name>lowerBound</name> <value><i4>18</i4></value> </member> <member> <name>upperBound</name> <value><i4>139</i4></value> </member></struct>
SOAP
Simple Object Access Protocol
“Competitor” to XML-RPC
Standardized by W3C
Now the de-facto standard for Web Services
Remote Procedure Call style
Electronic Document Interface (EDI) style
SOAP Adoption
Adopted by Microsoft for inter-application communication in .NET.
Built into C#
J2EE 1.4 requires JAX-RPC (Java implementation of SOAP)
Apple’s MacOS X 10.3 - AppleScript, Carbon, Cocoa
(Very) Basic SOAP Architecture
ApplicationCode
ClientCode
SOAP over
HTTPJava-specificcalls and datatypes
PHP-specificcalls and datatypes
SOAP Messages
Envelope
Zero or One Header
One Body
Any number of well-formed XML elements
Must be namespace qualified
Sample EDI-Style<s:Envelope xmlns:s=”http://www.w3.org/2001/06/soap-envelope”> <s:Header> <m:transaction xmlns:m=”soap-transaction” s:mustUnderstand=”true”> <transactionID>999</transactionID> </m:transaction> </s:Header> <s:Body> <u:grade xmlns:u=”http://www.ucsc.edu/soap/grades”> <u:year>2005</u:year> <u:quarter>Winter</u:quarter> <u:sid>W0001234</u:sid> <u:callnum>93819393</u:callnum> <u:grade>B+</u:grade> </u:grade> </s:Body></s:Envelope>
Sample RPC-Style<s:Envelope xmlns:s=”http://www.w3.org/2001/06/soap-envelope”> <s:Header> <m:transaction xmlns:m=”soap-transaction” s:mustUnderstand=”true”> <transactionID>999</transactionID> </m:transaction> </s:Header> <s:Body> <g:getGrade xmlns:u=”urn:gradeService”> <year xsi:type=”xsd:int”>2005</year> <quarter xsi:type=”xsd:string”>Winter</quarter> <sid xsi:type=”xsd:string”>W0001234</sid> <callnum xsi:type=”xsd:int”>93819393</callnum> </g:getGrade> </s:Body></s:Envelope>
String getGrade( int year, String quarter, String sid, int callnum );
Sample RPC-Style<s:Envelope xmlns:s=”http://www.w3.org/2001/06/soap-envelope”> <s:Body> <g:getGradeResponse xmlns:u=”urn:gradeService”> <grade xsi:type=”xsd:string”>B+</grade> </g:getGradeResponse> </s:Body></s:Envelope>
Structs
Arrays
RPC-Style Structs And Arrays
<student> <sid>W0009999</sid> <firstName>Jane</firstName> <familyName>Doe</familyName> ...</student>
<students> <student sid=”W0001234”/> <student sid=”W0009999”/></students>
RPC-Style Arrays and Binary
Double dimensional arrays, bounded and unbounded
Partially transmitted arrays with offset
Sparse arrays transmit only non-null positions
Position can be specified as null
Base64 encoding for binary transfer (JPEG, etc.)
RPC-Style Multi-Reference
<classes> <class> <enrollment> <student href=”#student-1”/> ... </enrollment> .... </class> <class> <enrollment> <student href=”#student-1”/> ... </enrollment> .... </class></classes><students> <student id=”student-1”> .... </student></students>
When things go wrong
What can go wrong
Network / Communications error
Incorrect target address
Service unavailable
Illegal service request or parameter
SOAP Faults returned for service-level errors
Standard SOAP Faults
VersionMismatch - between client and service SOAP envelope namespaces
MustUnderstand - required header block not understood
Server - Unknown error not directly related to message (Database)
Client - Problem with the message
Custom SOAP Faults
Defined by the service
Must be namespace-qualified
Use with caution since not all SOAP processors handle these gracefully
Extending standard faults is preferred
Example SOAP Fault<s:Envelope xmlns:s=”http://www.w3.org/2001/06/soap-envelope”> <s:Header> <f:Misunderstood qname=”abc:transaction” xmlns:=”soap-transactions” /> </s:Header> <s:Body> <s:Fault> <faultcode>MustUnderstand</faultcode> <faultstring>Header(s) not understood</faultstring> <faultactor>http://www.ucsc.edu</faultactor> </s:Fault> </s:Body></s:Envelope>
Talking to SOAP
SOAP describes the structure of the message, not the content
How do you pass a person’s name?
or
<student> <firstName>Jane</firstName> <familyName>Doe</familyName> ...</student>
<student> <name>Doe, Jane</name> ...</student>
Describing a SOAP Service
Web Service Description Language (WSDL)
Everything you need to know when interacting with that service
Sometimes generated on-the-fly, more efficient when static
Example WSDL<wsdl:definitions xmlns:typens="http://soap.amazon.com" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://soap.amazon.com" name="AmazonSearch"> <wsdl:types> <xsd:schema xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://soap.amazon.com"> <xsd:complexType name="ProductLineArray"> <xsd:complexContent> <xsd:restriction base="soapenc:Array"> <xsd:attribute ref="soapenc:arrayType" wsdl:arrayType="typens:ProductLine[]"/> </xsd:restriction> </xsd:complexContent> </xsd:complexType> <xsd:complexType name="ProductLine"> <xsd:all> <xsd:element name="Mode" type="xsd:string" minOccurs="0"/> <xsd:element name="ProductInfo" type="typens:ProductInfo" minOccurs="0"/> </xsd:all> </xsd:complexType> ...
Example WSDL <message name="RemoveShoppingCartItemsRequest"> <part name="RemoveShoppingCartItemsRequest" type="typens:RemoveShoppingCartItemsRequest"/> </message> <message name="ModifyShoppingCartItemsRequest"> <part name="ModifyShoppingCartItemsRequest" type="typens:ModifyShoppingCartItemsRequest"/> </message> <message name="ShoppingCartResponse"> <part name="ShoppingCart" type="typens:ShoppingCart"/> </message> <portType name="AmazonSearchPort"> <!-- Port for Amazon Web APIs --> <operation name="KeywordSearchRequest"> <input message="typens:KeywordSearchRequest"/> <output message="typens:KeywordSearchResponse"/> </operation> <operation name="PowerSearchRequest"> <input message="typens:PowerSearchRequest"/> <output message="typens:PowerSearchResponse"/> </operation> <operation name="BrowseNodeSearchRequest"> <input message="typens:BrowseNodeSearchRequest"/> <output message="typens:BrowseNodeSearchResponse"/> </operation>
Example WSDL </portType> <binding name="AmazonSearchBinding" type="typens:AmazonSearchPort"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/> <!-- Binding for Amazon Web APIs - RPC, SOAP over HTTP --> <operation name="ModifyShoppingCartItemsRequest"> <soap:operation soapAction="http://soap.amazon.com"/> <input> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://soap.amazon.com"/> </input> <output> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://soap.amazon.com"/> </output> </operation> </binding> <service name="AmazonSearchService"> <!-- Endpoint for Amazon Web APIs --> <port name="AmazonSearchPort" binding="typens:AmazonSearchBinding"> <soap:address location="http://soap.amazon.com/onca/soap2"/> </port> </service> <!--Shopping Cart--></wsdl:definitions>
Registering Web Services
Similar to P2P tracker ideas
Web Services are registered with Universal Description, Discovery, and Integration (UDDI) registries
Complex and frequently overkill
WS-Inspection is a lightweight method of describing web services in a given site
Web Services Security
No good standards yet (that I know of)
Use HTTPS for point-to-point encryption
XML Digital Signatures and XML Encryption standards may provide the necessary features
Compatibility
Not all implementations are compatible
Apache Axis 1.1 vs. Apache Axis 1.2
Section 5 Encoding
Microsoft .NET
Sample Web Servicepackage edu.ucsc.whisper.rpc;
import java.rmi.Remote;import java.rmi.RemoteException;
public interface RemoteUserService extends Remote{ boolean confirmCredentials( String username, String password ) throws RemoteException; boolean changePassword( String username, String password, String newPassword ) throws RemoteException;}
Sample Web Servicepackage edu.ucsc.whisper.rpc;
import org.springframework.remoting.jaxrpc.ServletEndpointSupport;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import edu.ucsc.whisper.core.User;import edu.ucsc.whisper.core.UserManager;import edu.ucsc.whisper.service.UserService;
/** * Provides a Web Services client interface to User-related services within whisper. */public class JaxRpcUserService extends ServletEndpointSupport implements UserService, RemoteUserService{ /** The user manager that provides access to user data in the system. */ private UserManager userManager; /** Logger for this class and subclasses. */ private Log logger = LogFactory.getLog( getClass() ); /** * Return the logger. * * @return The logger for this class and its subclasses. */ protected final Log getLogger() { return( logger ); }
Sample Web Service /** * Initialize the service. This function has access to Spring Framework contexts. */ protected final void onInit() { setUserManager( (UserManager) getWebApplicationContext().getBean( "userManager" ) ); } /** * Set the user manager that will perform backend tasks for the service provider. * * @param manager The UserManager instance to use. */ public final void setUserManager( final UserManager manager ) { userManager = manager; }
Sample Web Service /** * Confirm a user's credentials by matching the submitted (encrypted) password to the * (encrypted) password stored for that user. * * @param username The user whose credentials are being examined. * @param password The encrypted password being examined. * * @return TRUE if a user with the specified username exists and the encrypted password for * user matches the specified encrypted password, FALSE if there is no match, either * parameter is invalid (null or only whitespace), or no user by that name can be found. */ public final boolean confirmCredentials( final String username, final String password ) { boolean credentialsValid = false; if( ( username != null ) && ( username.trim().length() > 0 ) && ( password != null ) && ( password.trim().length() > 0 ) ) { User u = userManager.getUser( username ); getLogger().info( "Confirming credentials: username=" + username + ", password=" + password ); // To verify the credentials, the user must be found, have a password equal to the // submitted password, and the account must be enabled. credentialsValid = ( u != null ) && u.getPassword().equals( password ) && u.isEnabled(); } return( credentialsValid ); }
Sample Web Service /** * Change a user's password. * * @param username The user's username. * @param password The user's current hashed password. * @param newPassword The user's new hashed password. * * @return TRUE if the password was successfuly changed, and FALSE if the user's current * credentials could not be verified or if the new password was invalid (null, empty * string, etc.). */ public final boolean changePassword( final String username, final String password, final String newPassword ) { boolean success = false; if( ( username != null ) && ( username.trim().length() > 0 ) && ( password != null ) && ( password.trim().length() > 0 ) && ( newPassword != null ) && ( newPassword.trim().length() > 0 ) ) { User u = userManager.getUser( username ); if( ( u != null ) && u.getPassword().equals( password ) ) { u.setPassword( newPassword ); success = true; userManager.storeUser( u ); } } return( success ); } }
Sample PHP Client<?php
class User{ private $username = ""; private $password = ""; public function __construct( $username, $password ) { $this->username = $username; $this->password = $password; } public function getUsername() { return( $this->username ); } public function getPassword() { return( $this->password ); }
Sample PHP Client public function validateUser() { $userService = new SoapClient( $_SESSION[ 'config' ]->getServerBaseURL() . "ws/UserService?wsdl", array( 'trace' => 1 ) ); $confirmed = false; try { $confirmed = $userService->confirmCredentials( $this->username, $this->password ); } catch( Exception $e ) { echo "caught exception<br>\n"; print_r( $e ); echo "<p>\n"; echo "Request :<br>", htmlspecialchars($userService->__getLastRequest()), "<p>"; echo "Response :<br>", htmlspecialchars($userService->__getLastResponse()), "<p>"; } return( $confirmed ); }
Sample PHP Client public function changePassword( $newPassword ) { $confirmed = false; $userService = new SoapClient( $_SESSION[ 'config' ]->getServerBaseURL() . "ws/UserService?wsdl", array( 'trace' => 1 ) ); try { $confirmed = $userService->changePassword( $this->username, $this->password, $newPassword ); if( $confirmed === true ) { $this->password = $newPassword; } } catch( Exception $e ) { echo "caught exception<br>\n"; print_r( $e ); echo "<p>\n"; echo "Request :<br>", htmlspecialchars($userService->__getLastRequest()), "<p>"; echo "Response :<br>", htmlspecialchars($userService->__getLastResponse()), "<p>"; } return( $confirmed ); }}?>
Web Service Config<?xml version="1.0" encoding="UTF-8"?><deployment xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java"> <globalConfiguration> <parameter name="sendXsiTypes" value="true"/> <parameter name="sendMultiRefs" value="true"/> <parameter name="sendXMLDeclaration" value="true"/> <parameter name="axis.sendMinimizedElements" value="true"/> <parameter name="axis.development.system" value="true"/> </globalConfiguration>
<handler name="LocalResponder" type="java:org.apache.axis.transport.local.LocalResponder"/> <handler name="URLMapper" type="java:org.apache.axis.handlers.http.URLMapper"/> <!-- The soap monitor service should be commented out for production systems. --> <handler name="soapmonitor" type="java:org.apache.axis.handlers.SOAPMonitorHandler"> <parameter name="wsdlURL" value="/axis/SOAPMonitorService-impl.wsdl"/> <parameter name="namespace" value="http://tempuri.org/wsdl/2001/12/SOAPMonitorService-impl.wsdl"/> <parameter name="serviceName" value="SOAPMonitorService"/> <parameter name="portName" value="5001"/> </handler> <service name="SOAPMonitorService" provider="java:RPC"> <parameter name="allowedMethods" value="publishMessage" /> <parameter name="className" value="org.apache.axis.monitor.SOAPMonitorService"/> <parameter name="scope" value="Application"/> </service>
Web Service Config <service name="UserService" provider="java:RPC" style="RPC"> <requestFlow> <handler type="soapmonitor"/> </requestFlow> <responseFlow> <handler type="soapmonitor"/> </responseFlow> <parameter name="allowedMethods" value="*"/> <parameter name="className" value="edu.ucsc.whisper.rpc.JaxRpcUserService"/> </service> <transport name="http"> <requestFlow> <handler type="URLMapper"/> <handler type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/> </requestFlow> </transport> <transport name="https"> <requestFlow> <handler type="URLMapper"/> <handler type="java:org.apache.axis.handlers.http.HTTPAuthHandler"/> </requestFlow> </transport> <transport name="local"> <responseFlow> <handler type="LocalResponder"/> </responseFlow> </transport></deployment>