Since XML can represent structured data in an open, standard way, it is quickly becoming the preferred method of data exchange over the Web. In a year or two, it will be the dominant method. Sites that serve up information in HTML useful primarily to human eyeballs will add the ability to retrieve the information in an XML-based format that will allow other servers to use that data more easily. Businesses whose current web-based applications only allow human interaction through web-based HTML forms are scrambling to add the ability to accept posted requests in XML formats to enable Business-to-Business automation.
In this way, the Web will rapidly evolve to offer a business application developer a sea of XML-based information services, where your application can check on the status of an order, cancel a reservation, or book a flight simply by sending and receiving appropriate XML datagrams. It goes without saying that the ability to write programs that post and receive XML is a core competence for any developer building the next generation of Web applications. Here you'll explore how these key activities can be done with PL/SQL inside Oracle8i.
To be very precise, when talking about "posting XML to another server over the Web," what is meant is sending an HTTP POST request to that server, containing an XML document in the request body with a MIME Content-Type of text/xml.
Therefore, a fundamental ingredient in posting XML over the Web is the ability to send an HTTP POST request. Since the HTTP protocol is a set of conventions layered on top of TCP/IP, the Oracle8i built-in package called utl_tcp can be used to build the HTTP POSTing solution.
The utl_tcp package exposes the low-level routines necessary to open a TCP/IP connection, write data into the connection, and read data back from the connection. By writing the appropriate HTTP commands and data to the connection, the PL/SQL-based XML posting functionality can be easily implemented. For example, to post this XML document:
<moreovernews>
<article>
<url> http://www.xmlhack.com/read.php?item=400 </url>
<headline_text> XSL Working Group to address extension concerns
</headline_text>
<source> XMLHack.com </source>
</article>
</moreovernews>
to a Web service located at the URL:
http://services.example.com:2775/add-news-story.xsql
you need to do the following:
- Open a TCP/IP connection to the services.example.com machine on port 2775.
- Write the header information to the connection:
HTTP POST /add-news-story.xsql HTTP/1.0
Content-Type: text/xml
Content-Length: 240
- Write a carriage return to the connection to separate the header from the body.
- Write the XML document to the connection.
Then read the response from the connection. You can check out the full source code of the package that implements this HTTP behavior by clicking on xml_http package. Here, you just need to see the API for the http package, which looks like this:
CREATE OR REPLACE PACKAGE http AS
-- HTTP POST a document to url and return response
PROCEDURE post(doc VARCHAR2,
content_type VARCHAR2,
url VARCHAR2,
resp OUT VARCHAR2,
resp_content_type OUT VARCHAR2,
proxyServer VARCHAR2 := NULL,
proxyPort NUMBER := 80);
-- HTTP GET resource at url and return response document
PROCEDURE get(url VARCHAR2,
resp OUT VARCHAR2,
resp_content_type OUT VARCHAR2,
proxyServer VARCHAR2 := NULL,
proxyPort NUMBER := 80);
END;
This package lets you easily HTTP POST or HTTP GET any information over the Web.
Built on top of the post and get procedures in the http package, you can build another helper package called xml_http which makes posting and getting XML-based information a little easier. Example 1 shows the package specification for xml_http.
You'll see in the implementation of xml_http in Example 2 that the procedures here simply add the convenience of being able to directly post an xmldom.DOMDocument object as well return the response of a POST or a GET directly as an xmldom.DOMDocument for further processing.
With all these routines in place, it's time to put them to work. First, try posting a news story to a server that supports a "Post a New Newstory Service." Figure 1 illustrates the XML-over-HTTP exchange that takes place between the database server and the news story service.
The requester posts an XML-based news story in an expected XML format like the Moreover.com news format, and the service returns an XML-based message to indicate the status of the request. Here, the returning XML message only contains a status message, but the datagram sent back by the server could contain lots of additional useful information besides a status.
Example 3 shows a postNewsStory procedure that
- Concatenates the argument values passed to the function at the appropriate places in the <moreovernews> XML datagram
- Posts the news story datagram to the Web service URL using xml_http.post
- Tests the content of the returned XML document using xpath.test to see if the POST request succeeded.
You can quickly test the function from SQL*Plus by creating a SQL*Plus variable named status, and executing the function like this:
SQL> variable status varchar2(10);
SQL> exec :status := postNewsStory('It Worked!','Steve','http://someserver/somepage.html');
PL/SQL procedure successfully completed.
SQL> print status
STATUS
------------
Success
Printing the value of the status variable shows that the request was a Success.
Next, try an HTTP GET example. Sometimes, Web services simply take the information they need to carry out their task as parameters on a URL. In these cases, it is not required to post any XML document. Instead just do an HTTP GET on the service's URL with appropriate parameter values tacked on to the end of the URL.
Figure 2 shows the exchange between the database and a Web service that allows you to look up the name of an airport, given its three-letter description. The database running at the site offering this "Airport Lookup" service contains the three-letter codes and descriptions of more than 10,000 worldwide airports. Look up the code for any airport code XYZ by doing an HTTP GET on the URL:
http://ws5.olab.com/xsql/demo/airport/airport.xsql?airport=XYZ
To do this, you create a quick airportDescription function that:
- Concatenates the argument value passed to the function at the end of the Web service's URL
- Gets the datagram from the Web service using xml_http.get
- Tests the content of the return XML document using xpath.test to see if the POST request succeeded
Here is the code:
CREATE OR REPLACE FUNCTION airportDescription(code VARCHAR2) RETURN VARCHAR2 IS
description VARCHAR2(80);
proxyServer VARCHAR2(80) := 'www-proxy.us.oracle.com';
service_url VARCHAR2(80);
xml_response xmldom.DOMDocument;
BEGIN
-- This is the url of the XML web service to look up airports by code
service_url := 'http://ws5.olab.com/xsql/demo/airport/airport.xsql';
-- Do an HTTP GET of the service_url, tacking on the "airport" parameter
xml_http.get(service_url||'?airport='||code,
xml_response,
proxyServer);
-- If the Document Element is <Ok>, then return the description
IF xpath.test(xml_response,'Ok') THEN
RETURN xpath.valueOf(xml_response,'/Ok/Airport/Description');
ELSE
RETURN NULL;
END IF;
END;
Again, quickly test the new airportDescription function from SQL*Plus like this to see what airport corresponds to the three-letter abbreviation XML:
SQL> VARIABLE descrip VARCHAR2(80);
SQL> EXEC :descrip := airportDescription('XML');
PL/SQL procedure successfully completed.
SQL> PRINT descrip
DESCRIP
-------------------------
Minlaton, Sa, Australia
So using this Web service, you discover that to really travel to the heart of XML country, you'll need to fly Qantas.