Configure player

Close

WWDC Index does not host video files

If you have access to video files, you can configure a URL pattern to be used in a video player.

URL pattern

preview

Use any of these variables in your URL pattern, the pattern is stored in your browsers' local storage.

$id
ID of session: wwdc2002-804
$eventId
ID of event: wwdc2002
$eventContentId
ID of session without event part: 804
$eventShortId
Shortened ID of event: wwdc02
$year
Year of session: 2002
$extension
Extension of original filename: mov
$filenameAlmostEvery
Filename from "(Almost) Every..." gist: ...

WWDC02 • Session 804

Client Web Services Frameworks

Networking and Server • 51:52

This session introduces web services, with an overview of the toolkits and frameworks available in Mac OS X. An explanation of important concepts and terminology is followed by an in-depth exploration of Apple's web service framework, a client-side framework for accessing web services from Mac OS X. Techniques for writing web services glue and adding it to Cocoa, Carbon, and AppleScript applications are also demonstrated.

Speakers: Steve Zellers, Tim Bumgarner

Unlisted on Apple Developer site

Transcript

This transcript was generated using Whisper, it may have transcription errors.

Good morning. I want to make sure I understand how this thing works. This is me, Steve Zellers, and I'm an engineer working on web services frameworks for Mac OS X. So before we begin, let's talk about what a web service is, what it means to us and to developers. A web service is basically an endpoint or a method that you can access over the Internet using standard protocols, including XML and HTTP, to either get a result to perform some action on what we call an endpoint. This is usually an HTTP server.

It's heterogeneous. That means that the clients and the servers can be running on different architectures and they're all just going to speak a lingua franca called XML. There are commercial web services, which are things that you might pay for. If you had a business that needed some piece of information, someone else could put up a web service that provided that information and you would access it through a web services toolkit and you would be billed for it.

Then there are private or not commercial web services that you could also access. Some examples that you'll see on the net, Barnes and Noble has a web service that you give it an ISBN number, it gives you the price of the book. There's also a web service that tells you the current temperature in a zip code. Cupertino is 95014. You would send it 95014, it would tell you that it's a balmy 68 degrees or some such.

So the standards in web services, it's all built on acronyms. And there are dozens and dozens of acronyms, and I'm only going to talk about a few of these during this presentation. XMLRPC and SOAP are both formats of web service invocations. That is, these are kind of mutually exclusive, just different ways of packaging up the data that you're sending to the web service. These are both built on top of XML, which is a standard for presenting information in a way that is reliable. The other side is guaranteed to either understand the message or not. It's also very easy for humans to read so that we get an understanding of what the data is that's being sent. These are both, in our case, sent on top of HTTP, which we all know is used to access web pages on the internet and also to do HTTP posts, which web services typically are done via HTTP posts. There's another acronym called WSDL, W-S-D-L, which stands for Web Service Description Language. This is an XML document that describes what services are available at an endpoint. I'll talk about that a little more later. Here's an architectural diagram of what XMLRPC and SOAP look like sitting on top of XML. In addition, SOAP sits on top of XML schemas, which give you a very rich type system. There are actually two ways of encoding SOAP documents. The phrase you're going to hear a little later is called Section 5 encoding.

In this, the XML that you're sending is typed. It's strongly typed with a tag on the XML that says, "This element is an integer," or, "This element is a string." XML schemas let you build up complex types. Again, I'll talk about those a little later on. As you see XML writing on top of HTTP is typically what we think of as a web service.

So here's the state of XML web services today. XMLRPC is a spec that was developed by a company called Userland with some independent developers with help from Microsoft and some other folks. And basically, it was published by this company and is used by them and by other vendors to implement a simple RPC mechanism on top of XML and on top of HTTP post.

It is done. The guy who owns the spec isn't moving on it, And that means you can implement this back today, and it's pretty much guaranteed to work tomorrow. It's not a moving target. SOAP is an evolving standard, however. SOAP 1.0 was developed by UserLand and Microsoft and some other folks, and it's a little different from XMLRPC. The syntax is a little different, but fundamentally it's trying to do the same thing. And as SOAP evolves, more and more extensions have been added onto it. So as of today, SOAP 1.1 is a W3C recommendation, the World Wide Web Consortium recommendation, which means it's basically done.

It's no longer a moving target. SOAP 1.2 is a W3C note, which means it's still a moving target. It's still being debated. The extensions to the specification aren't final yet. In addition, SOAP includes something called a header, which can contain additional information for SOAP extensions. And SOAP extensions can be anything that a platform vendor might want to add. So SOAP is more of a moving target, but it's still being pushed very heavily by Microsoft.

So the difference between these two specifications. XmlRPC is very simple. And I'm going to show you an example of what the message actually looks like in a minute. Soap 1.1 could be as simple as soap, but it could also be written in a more complex way. It's kind of like Perl. You can write your Perl code any way you want. And it may look like line noise, or it may look like Java. And it's up to you to make that distinction. Both of them have basic type support for scalars, integer, string, double, and date, and binary data passed as base64. And it has support for custom types, arrays, and records. SOAP enhances this by adding multi-ref support so you could have a single element in an XML document that is referenced multiple times. And it also has user types in the form of XML schemas.

In XMLRPC, there is a major flaw, or what most of us think of as a flaw, in that the string parameters must be passed as US ASCII text. If you have international text that you want to pass, you have to encode it as base64 binary. And this has been a bone of contention for a lot of people, and it's just something you have to deal with in the API. XMLRPC passes parameters by position. If you have, say, a routine that adds two numbers, A and B, or, say, divides two numbers where the position is important, the XMLRPC routine has to take those parameters in order A, B. And the SOAP method takes them as named parameters.

You can say element A or parameter A has a value 4, parameter B has a value 2. XMLRPC can only be transported according to the spec, over HTTP. It's a simple enough format and there's no reason that you couldn't pass it over any other transport, but by and large the services that are available in XMLRPC are all available over HTTP. SOAP is transport agnostic. It has recommendations for, if you send it over HTTP, additional headers that you might add and how it might be dealt with by the server in terms of what error code it might return. But the SOAP message format itself is platform agnostic.

So how do you access web services? Well, there are dozens and dozens of toolkits for both XMLRPC and SOAP. And the thing to remember about these toolkits is that they bind a framework to the runtime model of your application. So if you have an application written in Java, it's probably a good bet to go with one of the established Java, SOAP, or XMLRPC implementations because they will match your object model. But it's an investment to bring one of these toolkits into your application. If you go out on the net and grab a toolkit, then start shipping it with your application, you're gonna be forced to maintain that toolkit, and your customers are going to have to hear about it. Or you'll hear about it when your customers are dissatisfied with some bug or something else. So you want to choose the toolkit that best matches your runtime environment and your application frameworks.

So what did we do in Mac OS 10.1? Well, last year at the conference, we were really pushing Mac OS X and what was available then. And coming out a few months later was Mac OS 10.1, and we couldn't tell you about one of the exciting features in Mac OS 10.1, which is XMLRPC and SOAP 1.1 support baked into the Apple Event Manager. Because it was baked into the Apple Event Manager, AppleScript got support for this for free. In a few minutes, we'll have a demo of that. This proved to be a very popular way for people on our platform to access these web services. They didn't have to go out on the net and download a toolkit and make sure their customers had it. They just had to make sure their customers had 10.1, which most everybody did, and they were able to write applications that took advantage of the support built into the system. It works by hijacking an addressing mode for an Apple event. An Apple event, for those of you who are new to our platform, is an inter-process communication mechanism. In 10.1, we reintroduced remote Apple events, the ability to send a binary message to another application on another computer, another Mac OS X computer.

For addressing modes that were HTTP, we treated them as a web service. We were able to do this without any API changes to the Apple Event Manager and just by enabling some additional data types that hinted to the Apple Event Manager that the method that needed to be called was to be called using the XMLRPC or SOAP framework.

So I'm going to give the canonical XML RPC example, which is to call the method examples.getStateName on the server http://betty.userland.com/port80/rpc2. When I go to send this message, I'm going to open up an HTTP connection and do an HTTP post to that site with an XML document. The XML document takes a single integer as a parameter and returns a string. This is what that message is going to look like.

It's XML. It has a structure to it that includes the method name and a parameter list. In this case, there's a single parameter. You can see the parameter is number 41 encased in an element tag that says I4. That's XMLRPC's indicator that the element is typed as a long integer. And in response, the server comes back by saying, "Okay, the return value from that method is the string South Dakota. Now the server is returning, I think, basically it's got an array of 50 states and it returns element 41 in the array in no particular order. It's not a very interesting example, but it shows the message and response pretty well.

So I'm not going to do this on the computer. We're just going to go through it on some slides. But how do you build an Apple event that targets a remote site? So it's just Apple events, just like you would use to send a message to the finder. First thing you need to do is you create the parameter list for the method call. And in our case, the parameter list is a single integer.

So these are the inside Mac 1992 calls to create a parameter list and put the value 41 in the parameter list. It's easy stuff. all in inside Mac. Then you create the direct object. In the Apple event implementation of web services, the direct object contains both the method name and the parameter list. This is handed off to the subsystem that deals with the RPC calls. So the direct object for a SOAP web service call or an XMLRPC web service call contains two fields: the keyRPC method parameter field and the keyRPC method name field. So the parameter list that we created in the last slide is stuck in the direct object and the method name that we want to send to the remote site is also set in the direct object as a string.

So the next thing we do is create the event that we're going to send. And just like in the normal Apple Event Manager, you create the target address. In our case, it's an application URL. Type char should be on that slide. And then you give it the location that you're going to post the event to. Then you create the event with the special key, with the special event class and ID of KAERPC class and the event ID of KAEXMLRPC scheme, which is a horrible name, but makes sense when you look in the header. There's also KAE soap scheme. So you can see that you can pretty much use the same exact code to call a soap method that does the same thing.

So this is what we've created. We've created an Apple event on the left-hand side of the screen, and the callouts show you basically what the contents of the Apple event look like. You've got the class, the scheme, the target address, and the direct object is broken out into a record with two fields. Thank you. The examples.getStateName is the method name. The parameters is actually a list containing the number 41.

Okay, so then you send the event, and you deal with the reply. Apple events can be sent synchronously or asynchronously, which means that if it's an asynchronous call, you're going to receive the reply as an Apple event, as a class, A-E-V-T, A-N-S-R. But in this case, we're just going to say, send the event, wait for the reply. This opens up the connection to the remote site and sends the event, gets the reply, turns it back into an Apple event descriptor, and then you can pull out, out of the reply, the direct object, which in our case is a string, and then you can call printf because your Unix programmer is now, and you can call printf with impunity.

You can have more complex types in an Apple event. You can have an AE list gets turned into an array in the XML, and a record gets turned into a structure, which is a key-value pair. The problem with Apple event records, though, is that they have a limitation that the key for a record is a 4-byte string, or an OS type, actually. It's a 32-bit integer. So we borrow something innovated by AppleScript, which is to say you can have a list of items in a record under the key key_as_user_record fields, and this will be the string and then a descriptor and then a string and then a descriptor. And this is how you would build up a complex record in AppleScript or in the Apple Event Manager. So when the Apple Event Manager sees one of these special keys, it knows that it wants to build a structure with a complex name, a long string name, and then some value. And the value is gonna be either a scalar type or a compound type.

This is how you would do that. You create the list that this component is, I mean, let's call it a property list. The list of properties that are going to be in the dictionary, and then you just keep appending to it. Long string, value descriptor. Another key is a long string, value descriptor. Put it into the record as a key AS user records fields, and the Apple Admin Manager will take care of the rest. Now, that's a lot of code to write, and there's an alternative, which is that AppleScript basically got support for all of this for free by the addition of a simple dictionary that adds the call soap and call XMLRPC methods. And this is a great way to prototype and debug web services because the Apple Event Manager sits below AppleScript, and AppleScript just builds complex Apple events like a duck in water. It's really easy to use AppleScript to both prototype and to embed web services inside your application. There is a tech note that I'll mention at the end that can show you how to add AppleScript to your application as well. So at this time, I'm going to ask Tim Bumgarner to come up and demo AppleScript Studio, which uses Apple Events to access web services.

Sound? Sound? There we go. Okay, great. Okay, first thing we need to do in our demo is actually discover what type of service we want to use, and I've got an application here called X Methods Service Finder, and this employs the services from the X Methods website to actually display all the different publishers, the service names, and a short description of all the services that are available. And I've got a nice little search area up here. I can actually find, oh, maybe all the publishers that begins with X methods. I'll type this and it quickly just lists the ones that are from X methods. Let's do something a little more interesting. Let's see if we can find anything that has to do with stock. So if I, maybe I wanted a web service that would pull out the current value of a stock, I could use that. For our demo, actually, let's see if we can find anything about Dilbert. And actually there is one, Daily Dilbert. And it looks like it says it returns a binary stream of today's Dilbert Comics. And actually what it returns is a URL to an image. So let's go ahead and build a web service built on this particular service. So I'm going to go into Project Builder.

And we're going to start right at the beginning. We're going to do this right from scratch. So I'm going to choose New Project. And in this case, I'm going to choose the Apple Script Application Template. And we're gonna go ahead and call this Daily Dilbert. Alright, click Finish.

Now I've got my project open here. And it's already given me basically a blank script to start with and pretty much just a start on our resources. If it will open-- there we go. Double click that. We're going to go into Interface Builder. First thing we're going to do is go ahead and put together our interface. And in this case, it's going to be a fairly trivial application.

We'll just make this a little smaller. I'm going to bring up the attributes. First thing I want to do is go ahead and change the title to Daily Dilbert of my window. Since what we're going to be doing getting an image and putting it and loading it in this view, we'll need an image view. There's one right here. Drag it over. And we'll change some of the attributes of that. We'll make it so there's no border. I can use the Aqua guidelines to make sure I get it snapped right up to the edge.

A couple other things is that we're going to automatically resize the image, this window, based on what the image that we get from the service. And so I want to just make sure that this particular view stretches. So for the most part, the UI is done. It's very simple. Now I actually want to add some AppleScript functionality. And one of the things we have to do is to name our objects so that we can refer to them by name in our scripts instead of by index. So I'm going to go to my AppleScript panel. Anytime I want to do anything with AppleScript in Interface Builder, I go to this panel.

And I'm going to name my window, go ahead and we'll call it Dilbert. And then I also want to name my image view. And I know that I've got the image view because when I click on it, there's an NSImageView title here. And I'm going to also call this one Dilbert. Now, what I want to do is I want to actually execute some AppleScript. So when this window gets loaded, or in particular this view gets loaded from the nib, I want to trigger an AppleScript. And so there is a nib class here. And for those of you that are Cocoa programmers, this will look very familiar, awake from nib. So you can attach to any object and find out when it's been loaded. And we're going to assign it to this.

One other thing is that I don't want the window to be initially visible. So let's turn off its visibility. And then the other thing we need to do is we want to know when the app is finished launching so that we can then present the window. So again, I go back to my Apple script. And in this case, there is a launched.

events. And let's go ahead and then click on the edit script. Now what this does is it goes back into Project Builder, loads my script, and it determines that there weren't any handlers with those names, and so it automatically goes and adds those handlers for me. So let's go ahead and I've got a little bit of a demo assistant here, if it's working correctly. It's going to enter the script so you don't have to watch me painfully type this. Basically what we do is the parameter to this web service is a date. And it has to be in the format month as digits slash day slash year. And ironically enough, I found it was actually easier to use the shell to call date to get it in that particular format because AppleScript doesn't make it easy to get it in this specific format. You can get it, the month comes back as the actual string of January or February. In this case, that wouldn't work. So I just found that we actually have this functionality built into AppleScript called do shell script.

And I just jump out and call the shell command date, passing it the parameter, and voila, works. So let's go ahead and continue on. Then the next thing I do is I want to get the image as this image view is being loaded. And so there's a handler here, get Dilbert image for date. And we're going to add that in just a bit. Now continue on. And then what I do is once I've loaded the image, I go ahead and set the image of that image view. And the thing that's being awoken, in essence, is the image view. The object will contain that image view.

All right, so let's go down and add the rest of our handlers here. Oh, we've got one more thing here. The next thing I want to do is once I load the image, I want to adjust the size of the window based on the size of the image itself. So I simply call size of the window. I set its size. And this is actually doing another interesting thing. It happens that at this point in version Studio 1.1, the size of an image can't be determined directly from AppleScript. This will be fixed.

But I can actually call Objective-C. And this is another Studio enhancement such that call method is calling the Objective-C method called size on the object, the image, without any code, I can just jump off and call into objective C. So if studi hasn't provided some functionality for you, I, we've sort of put a back door in with the call method. So let's go on down here and we'll see if we can put in the rest of it. Nope, and I forgot to show it. Keep forgetting one line. Okay. And then at the very end we want to show that window.

And then I'm going to go through this, the getDilbertImage. So it's setting it to some known value to begin with. And then we're going to call another handler called loadImageAtUrl. And this is going to call yet another handler calling the actual web service, getDailyDilbertForImage, the path. And we'll finish this. And then here's the loadImage. And I'll walk through this one here in a second. Basically, it does another call method. I cheated here again. Given the URL, there's actually a Cocoa-- There is a, in a string class, and there actually has one, a category put on it so I can actually get the last path component of that URL very simply. So I jump out and with call method I pass this particular method name on the URL and it gives me the last part of it because that's what I'm going to use as the part of the name. And basically we're going to stick this image that we load in /tmp so the next time we reboot we don't have to worry about getting rid of it. And then again we're going to employ do shell script because we can use the curl tool to pass it that URL and it's going to return and save it for me on my disk. And then when I'm done, I will use the load image command that's in studio to load that image and set it into that, or to pass it back in our array. All right. Then the next thing we need to read is the most interesting one as far as the web services is concerned. And this is called Daily Dilbert Image Path.

And what it does is it simply uses that URL that Steve talked about. This is the endpoint. And just like we talked to a local application, by tell application finder, we can simply talk to the web services tell application this particular URL. And then we give it the method name. And it needs also a SOAP action in this case and the namespace for that. And the parameters we're actually passing in right here in the end parameters, and they get sort of put together. And then we ultimately call SOAP. And this is going to do what Steve talked about.

It's going to take all this data that we passed, which is really just an AppleScript record. You'll see this is an AppleScript record. Takes that, pulls it into, or takes it and converts it into XML, sends it over the wire, waits for the response. It comes back as an XML data, and then he parses it and then gives it back to us in AppleScript so that we can use it. And that will be our image. And let's see. I think we've got everything that we need. Let's go ahead and save and run this. See how it works.

Builds, compiles. There you go. So it's went off, loaded that data, loaded our image, and it looks like that's our today's daily image. And Steve, pick it back up. Oh, by the way, I forgot to tell you. Just happens to be that this application itself is a studio application. The app that I use to find the services is a studio application. And here's the code for it, if I can open it up. It's actually going to talk to mail.

and close all the windows. And here is the service detail. So here I put this together last night. And here's the script for that. So just a little bit of script here. That whole application was written in a couple hours to actually do the services. Again, employing web services to get all the data that we needed. All right, thanks, Steve. Thank you, Tim.

So I guess the point to get out of that is that AppleScript and Apple Events empowered with web services really give you a way of getting to web services very quickly and at least, if nothing else, playing with them. Okay, so what do we have for you in Jaguar? We've decided to ship something called Web Services Core. Now what was wrong with the Apple Event Manager way of doing things? Nothing. The Apple Event Manager way of doing things is fine, but it's at a little too high a level in the protocol stack, in the library chain of our system. So in this slide, we see that App Services has the Apple Event Manager sitting as a little bubble in it. And then what we've done is implemented WS Core inside of Core Services. Now, this is a lower-level framework. It sits alongside of CF Network, Core Foundation, and Carbon Core, and it gives you access to the basic runtime model for our system. It's available to all applications, plugins, daemons, tools. It doesn't have any dependency on the Windows server or login window. You don't have to be a logged-in user. You could use it from a CGI application. Thank you.

Yeah, okay. So it's integrated with the system. Like I said, it sits inside of core services and it leverages CFXML parser and CF network. It's thread safe, run loop safe, and friendly. It encourages you to asynchronously prompt for... asynchronously issue invocation requests on the run loop and receive a reply on your run loop. It's CF type based, so you have to create CF type objects for your strings and integers and records, dictionaries, and CFArrays. But if you're an Objective-C programmer, you get toll-free bridging with the Objective-C types. It's also a simple procedural API, so there's no need to have a lot of objects involved for this API. There's actually probably six calls in the API and a single object or type ref.

So some of the features. The API is protocol agnostic, protocol independent, XMLRPC or SOAP. It's going to look the same, and the difference is going to be in what properties you set on what we call an invocation, a WS method invocation ref. I'll show you that in a second. You can have synchronous one-shot operation or asynchronous with callbacks. It's based on advances in CF Network. So as CF Network improves, web service WS Core is going to improve. Right off the bat, we get IPv6 support for firewalls and proxies, and it gives you full access to the underlying CF HTTP message.

When you go to the CF network talk, they'll talk more about what's inside a CF HTTP message, and this is how you would customize the behavior of the invocation request. It's conceptually similar to CF Stream. When you go to the CF Stream talk, you'll learn how to schedule reads for a CF Stream or how to deal with callbacks from a CF Stream. This model is a little simpler in that the synchronous calls give you a single callback with the result of the invocation. 1.0 comes with HTTP and HTTPS support.

Future versions of this framework will probably support other transport modes like SMTP or DIME or whatever becomes a standard for sending large chunks of XML data. So the one object that we talk about in the Web Service Core API is a WS method invocation ref. And the names get worse from here, so I'm just letting you know. This is created by calling a method inside the framework, giving it a URL and a method name, and a prototype parameter that specifies whether it's an XMLRPC or a SOAP message that you want to send. Then you add properties to the invocation ref that can control some of the serialization options, such as the SOAP action or method namespace. And then you set the parameters on the invocation as CF types. Then you either schedule it on a run loop or you call it synchronously, and it gives you back a result dictionary. I'M SORRY.

So, like I said, you create one of these things using the method, WS method invocation create. Takes a CFURL or an NSURL. A method name is a CFString. And the protocol is one of these CFString constants that are in the header. The header, by the way, on the Jaguar CD is WS method invocation dot H.

Then you set the parameters on the invocation. It takes two parameters into this function to set the parameters, a parameter dictionary and the order that the parameters should be serialized. For SOAP, the order isn't supposed to be important, but there are some services that require that the order be present. So it's probably a good idea to pass both of these values.

The CFDictionary contains the name of the parameter and a CF type for the parameter value. And of course that CF type can be one of the primitive CF types or it can be a compound or complex CF type. The types that we serialize automatically for you are CFBoolean, number, date, string, data, that second one should be CFData, whatever, CFArray and CFDictionary. You can also add meta keys to a CFDictionary to control the dictionary namespace when it's serialized for SOAP and as well as the parameter order. It's unfortunate, but there are some SOAP implementations that require that the parameter order be correct.

Now the types are serialized using what I referred to earlier as Section 5 encoding. The type information for custom types is not present in the invocation. And if you have custom types, you have to write your own dictionary-marshalling code, at least until a WSDL parser comes along that does that work for us. And I'll touch on that in a few slides.

So this is how you would build the dictionary and parameter order array. You call CFDictionary add value on a mutable dictionary that you've created. You've got two parameters. Param one is Steve, param two is John. You add the parameter names to the parameter name array, and then you just set the parameters onto the ref.

So then you invoke it, and you do that with a single call, ws method invocation invoke. The result of this call is a CFDictionary. Now, that's not the actual method result. That's a dictionary containing the actual method result as well as optional debugging information, including the outgoing XML and the returned XML and the HTTP headers or HTTP errors, and fault information so that if there was a networking transport error, that information is going to be in there and available as keys in that dictionary as well. So basically you make this invocation call, and then if you can call this method ws method result is fault, passing up that result dictionary, then you can pull out the fault string out of the dictionary and display it to the user or do some other work, whatever you want to do. Otherwise, if it's not a fault, you look up the kws method invocation result. The result of this is a CF type ref, and depending on the service you're calling, it's going to be some sort of value that is expressed in CF type lingo. So in the example we saw earlier of XMLRPC, the result of this would be a CFString. A more complex type like the X methods inspector that Tim showed, the result is a dictionary containing arrays of dictionaries containing arrays of dictionaries. And so you have to know up front what the type is. Of course, you can use introspection to decide what the type ID is and do something based on that.

You can also execute these asynchronously. You set on the invocation a client context. This is similar to how CF Stream works. In the old world, we used to set refcons. Nowadays, we set client context. This includes the info pointer that you want to pass to your callback, as well as some callbacks for the info pointer to allow it to be reference counted. And if you're familiar with the CF APIs, you can see how this works. Once you've set the callback, you can schedule this invocation on one or more run loops, and as those run loops execute, the invocation will pass through its state machine, ultimately resulting in getting the data or getting a fault, and then it will call your callback during that run loop invocation, and you can process the result. Okay, so we're going to do a demo now, and if I could do a demo for.

We're on demo four. All right. Here we go. Google API in shiny metal. I did the demo last night, so I had the opportunity to check that checkbox and make this thing metal. So I'm going to show you first what it does. So if you haven't heard, Google released an API for their web service, for their search engine. And this is relevant because if they don't change the API, which they shouldn't because APIs are supposed to be immutable, 20 years from now, I won't have to try and figure out how to scrape their web page to get their search results. I can just call this API and get the same information back. We search for Google API, hit return, and it goes out, contacts Google through WS Core, and fetches the results. The result from the Google API was a dictionary containing an that you see here. I'm not actually going out and fetching more information with each of these. But it contains a dictionary with a summary field and a URL field and an extra info field, which is what you see here. It's too small to read, but that's not the important part. So let's look at the code because that's usually more interesting. Now the example is written in Cocoa and just ignoring that, it's just C with brackets. First thing we do is we create an invocation. Well, actually, the first thing I guess we did was that we created a URL that specified the endpoint that we wanted to send our request to. In this case, http://google.com/search/beta2.

When you go to Google and you look at their API, they give you a developer kit, and it's got all the information about what the methods are available and what the APIs are, what the endpoints are, and sort of all the meta information you need in order to send the request.

So we create the URL, we create the invocation with the URL, the name of the method, and the protocol we're specifying as SOAP1999. It's probably not the best name, but that specifies SOAP1.1. Then we're going to set the parameters. The Google search takes ten parameters. They're all required in their API or in their implementation of the API, But that is something that... it's really up to them to own that. Probably the least relevant thing that's required is the key. When you sign up for their developer program, they give you a key. The key is like a 40-digit long number, alphanumeric string. I keep it in a dot file in my home directory so that all my tools can use it. We're going to do two things. We're going to build up an array of strings and an array of values. is a one-to-one correspondence between the key in the string table and the value in the value table. And in this case, we've got the keys, get my secret key from my dot file. Q is the query, the f query string. This was set when you hit the search button, magic Cocoa things happened. I have to create an NSNumber instead of just passing the number zero or 10 in here. There's no automatic Cocoa or Objective-C coercion from a scalar to an NSNumber. So I create the number with the integers zero and 10. There are additional parameters which are booleans and strings that tell Google what kind of search I want to do or whether to restrict the Google search to a particular language. I create an array which is the parameter order from the array that I tonsed up up front, and then a parameter values uh... dictionary using both the keys and the values from those static tables i set the parameters on the invocation basically copies them into the invocation so soap has a couple of additional pieces of information you need for the soap request there's the soap action header this is passed in the http message or an http header of the post and the api documentation from google said your soap action header must be this And so that's what this section here is saying. Additionally, there's something called the method namespace, and you have to set that as a property on the invocation using kwssoap method namespace URI. with, again, the string that was given to us by the Google API documentation. And then you invoke it.

So the result of the invocation is a result dictionary. So we block at this point, send the message, get the result, and then we process it in here. If the reply is a fault, then we're just going to go ahead and say an error occurred. Otherwise, we delve into the dictionary, and we look up the magic key KWS method invocation result. This contains the actual result of the SOAP invocation, not the meta information. Google, of course, well, not of course, but Google decided to add an extra level of an indirection to that. So we then, we know the result of that invocation was a dictionary, and then we have to look up the key return, which gives us another dictionary, and then the key result elements, which is an array that lives inside that dictionary. from there we just go off into the rest of our Cocoa class and process the results now one interesting thing is So, when we ran this API, Apple script, When we ran this demo, my little progress bar didn't spin. Why didn't it? I don't believe that.

A little progress bar didn't spin because this application blocked in order to get the results. I want to go ahead and modify it real quick. to do asynchronous, and this is actually really easy to do. So going to this other file, I'm going to copy the code that sets the context to my Cocoa object and sets the callback to be a little stub that calls a method on my Cocoa object.

Then WS method invocation schedule with run loop, giving it the current run loop. And then I'm gonna return from my function. So this is the function that gets hit when you call doSearch, right? here and instead of invoking, we'll just paste over that. close our function, and then we need another function which gets called when the callback completes.

that and paste. So what we've done is we've broken up that routine that got called from doSearch into two routines, one of which issues or schedules on the run loop and the other receives the reply and processes it. So now when we search for Google API, we get our little progress delay spinning and we've become an asynchronous application and we're no longer blocking, it's more responsive. Okay, so back to the slides.

There are a couple of customizations you can add to an invocation. You can override how it serializes CF types, and you do this based on the CF type IDs. So if a CFDictionary comes along that you need special control over serializing, you can say, I want to customize CFDictionary serialization and insert your own code at that point, which will get called to write out the XML. And of course, if you return null, then the default serializer will take over. Same goes for deserialization. If there's a part of an XML tree that isn't being parsed properly or to your satisfaction, you can get control of the parser at that point and replace your own CF type with what you think should be represented. And you can access the SOAP headers and put your own SOAP extensions in, or if there is a SOAP extension that you need support for that we don't support, because we don't support any in 1.0, you can add your own soap headers, which are XML trees, basically, into the soap header that gets sent.

Now, WSDL support. We looked at doing a WSDL API, and we weren't entirely satisfied with it. And if you talk to... Sabra Tran's talk yesterday, he talked about how the API process is iterative, where we sit on an API, and before we can really release it, because it's something we have to support forever, we have to work with it internally. So 1.0 does not include a WSDL API. We do include a tool called Developer Tools WSMakeStubs, which is on the Jaguar CDs. This parses the WSDL and produces stub templates for AppleScript, Objective-C, or C++. There are various degrees of complexity in the generated stub, so I think the C++ emitter is pretty much bare bones on top of the straight C API with just a little bit of structure for abstracting the creation of the invocation ref. The Objective-C version is a little smarter, and it has support for method invocation callbacks on a selector. And the AppleScript one is, of course, the highest level, but it gives you the least amount of flexibility as far as customization of the underlying invocation. The AppleScript one sits on top of the Apple Event Manager, sits on top of WS Core. The stubs that are generated adhere to Section 5 encoding again. We don't yet do anything with the XML schema information, So we're not creating full-fledged Objective-C types yet.

So, one more demo. Back to the demo machine. And if this works, whoa. Let's get rid of all these. I've got a new project. I'm not going to copy and paste. I'm just going to hope it works. I probably should have run it beforehand. I'm going to show you, I have in a PB project, I have a new project.

an invocation of the WSMakeStubs tool. It's probably too small to read, but it takes -x, Objective-C is the language that I want emitted, and then some parameters that specify the output directory, the name of the file that I want to create, and in this case, the URL that Google supplies with their WSDL information. And I'm going to go ahead and run it or compile it while I'm actually looking at the source.

Where is this source? There it is. So the header file that it generated for the Google API for Objective-C-- like this. And for each different method or endpoint that's available, it creates an Objective-C class, which inherits from a master class that implements some of the underlying WS core behavior. So doSpellingSuggestion is one of the Google APIs. And just like the other ones, it's got the key parameter and then the phrase that we want to spell check. and then the one that we were calling before, do Google search. So, in order to call this API, I changed my implementation to the API to rather than where you saw I had those two arrays, an array of values, an array of keys, I replaced that with a single call to create an invocation object from the generated stubs and then call the set parameters method on it, giving it all the same values, but this time it's statically bound, statically typed to the Objective-C runtime. I set the callback based on the set parameters method.

The API of the generated stubs gives you a set callback method and a schedule on run loop method. So it looks exactly the same as the other code. It's just I didn't have to do all the method namespace URI soap action and naming of parameters. That was all done by the generated stubs. So then when we ran it... It was running. It is running. So it's exactly the same thing, but the complexity of calling the API is abstracted by the generated stubs.

And it works. Yeah, it's even more important. Okay, and the source to that will be available just for demo, for trying to understand it. I can point you at that later. All right, so back to the slides. Where are we going in the future with this API? We're going to have some SOAP extensions coming in the future, and as these become formalized by the industry at large, we're going to adopt them and produce APIs to allow them to bind into WS Core. This is the core stuff. This is the stuff that we're going to build our web services architectures on. We will have a WSDL API in the future and better WSDL integration with CF types and CF schemas. People have ideas on how they would like to see that implemented, by all means get in contact with us and let's work together on that. There's been some call for a UDDI API. UDDI is an acronym that I'm not even going to try and remember. But it's for locating services on the network. for saying I am interested in services that give me cartoons and coming back with a list of WSDL files that specify different cartoons that you can get. So it's a programmatic interface for discovery of services. And a server-side API may be part of WS Core, but today WebObjects provides a server-side web services API that's very well integrated with the WebObjects environment. Okay, and Tom?

you want to talk about the roadmap or other sessions. Or I'll talk about them. Okay. So some other sessions that you might want to go to. CF Network that WS Core is built on is a very exciting framework that we're putting a lot of investment in and all the other sort of I-app and web projects that you see us doing at Apple are going to be built on CF Network. XML and WebObjects is going to talk about accessing web services from WebObjects and how to integrate XML into WebObjects applications. Same with Java Web Services. Managing I/O, CFRunLoop, and CF Stream, if you're a low-level person, you want to understand how CF Network works, how Web Services Core works, that's a good session to go to as well.

More WebObjects and Web Services. And then the AppleScript Studio intro. There'll be a lot of good information there about how to use AppleScript to access Web Services. And then who to contact would be Tom Weier, who is the networking and communications evangelist. Do you want to say anything, Tom? No? We didn't rehearse this.

more info. Well, so on the Jaguar CD in developer examples web services, there's a Xmethods inspector app built using WSMake stubs. It's not as nicely polished as the one Tim showed, but at least the source is there and it's easy to understand. You can find out about XMLRPC at the website of the developer, xmlrpc.com. He's got links to implementations as well as services that are available. SOAP is a W3C thing, so that's a URL that you use to access the the SOAP specification. Same with WSDL and UDDI. And there's a QA, Tech Note 1111, which is how to add OSA or AppleScript to your application. So if you want to take advantage of AppleScript today and web services in a shipping application, that would be a good place to start. There are also Tech Notes available on the developer public site on accessing web services