A Web Application

In this section, you develop a web site that is integrated with the XML Switch and the other systems running on your network (even if you’re putting this all on one machine). In this web application, a user navigates through some HTML on the server, allowing them to login (in other words, retrieve their profile), edit their profile information, or view special offers. A CGI script runs next to the HTML, and is called by the HTML forms and other items to retrieve the information.

Typically, a web site or CGI script accesses a database directly to pull information out on behalf of a user. However, this application is different. In this web site example, the CGI script uses the xsc client created earlier to talk directly with the XML Switch. So instead of SQL queries, the web site submits XML messages to the switch. The switch then maps these to objects and invokes the correct method, resulting in the right information being either inserted or pulled back from the XML Switch. The returned information is then parsed and formatted into HTML, and displayed back to the user.

Connecting to a Web Service

This is how many web services may first be implemented. For example, with Microsoft’s forthcoming web services (codenamed HailStorm), an independent business should be able to create a web site, as well as retrieve your user’s HailStorm calendar by issuing SOAP calls from your web server (or the client) to Microsoft’s services. The returned XML is then formatted by your web site (even “branded” with your logo) and returned to your user’s browser. The application presented here follows the same type of bridged-services scenario, minus some considerable security and error handling.

Figure 10-4 shows a diagram focusing on the web site, and its relation to the XML Switch as well as the rest of the network.

A detail of the web application
Figure 10-4. A detail of the web application

What Figure 10-4 doesn’t show, and perhaps shouldn’t, is that the XML Switch is in turn connected to other network resources, acting in effect as a black box to the CGI and HTML that just ask the Switch for data.

The Components

The Web/CGI application presented here is a standalone web application. It’s fully integrated into the XML Switching-network built thus far. This web site uses an HTML page and a Python script on the server to generate dynamic data. The CGI script uses the XML Switch client to retrieve information from the switch and its backend resources. This web site has no idea that profiles are kept in SQL Server and that offers are kept in a big flat file. It only know that it formats an XML message a certain way, and that tossing the message to the XML Switch usually results in the right information being returned.

We deployed the HTML and CGI scripts to a five-line CGI server we wrote in Python. Having used Apache and IIS throughout this book, we sought some cross-platform simplicity in this chapter and used a very simple CGIHTTPServer in Python. The HTML page intro.html, and CGI script sp.py, should run under any CGI-capable web server.

In this section, we develop the following pieces:

intro.html

This is a simple login page that accepts a user ID and posts you over to sp.py. The login box expects an ID that is valid within the database connected to the XML Switch. In other words, you need to put a valid ID here that corresponds to a record that you created in your database in Section 10.3, earlier this chapter.

sp.py

This is a Python CGI script that retrieves information from the XML Switch and formats it for display. It is not named .cgi, although you should rename it that if your CGI server is only going to run .cgi files. This script is the main workhorse of the application, as it not only retrieves the data for you, it formats it into HTML and sends it back to the browser.

When putting these finished files in your web server area, remember to put sp.py in a cgi-bin directory beneath intro.html. The web form used in intro.html and sp.py expects sp.py to live at /cgi-bin/sp.py.

Additionally, you should copy over xsc.py and XMLMessage.py (created in Section 10.6, earlier this chapter) into the cgi-bin directory so that sp.py is able to load them as modules.

The Topology

As shown earlier in Figure 10-4, the CGI application is primarily a series of transactions between itself and a web browser. However, when the user requests dynamic information, the CGI script uses the XML Switch client to talk to the XML Switch on the network. This example has broad network implications: intro.html and sp.py are served up by your web server; the sp.py script uses the XML Switch client (xsc, created in Example 10-11) to connect back to wherever you are running the XML Switch server process. As long as the site’s CGI script tells the Switch client where to find the Switch server, it gets the information it needs. Of course, an end user’s browser needs only to point to the web site.

In the next few sections, we highlight some of the more XML-centric code used in building the site, and present full listings of the three primary files.

The Code Architecture

When creating any web site, the very first thing to do might be to code a start page. For the purposes of this chapter, the start page is intro.html. The intro.html file is a simple page that politely asks for an ID. Use the submit button to send your ID off to the server, and the information is returned and formatted by the CGI script, which presents you with additional, but dynamically created, HTML. Figure 10-5 shows intro.html loaded in a web browser.

intro.html is the start page for the CGI example
Figure 10-5. intro.html is the start page for the CGI example

As Figure 10-5 shows, intro.html is straightforward. Probably the most difficult part is tracking down a valid ID from your Profiles database. Example 10-14 shows the intro.html.

Example 10-14. intro.html
<HTML>
<HEAD>
<TITLE>SuperUltraMegaCorp Shopping Portal</TITLE>
</HEAD>
<BODY>
<P align="center"><h1>SuperUltraMegaCorp Shopping Portal</h1></P>

<P>Greetings, if you haven't logged in, please take a minute
   to do so.  This site is personalized, and your personal
   information along with special offers are made available
   thanks to our XML processing infrastructure built and 
   powered by Python.  This web site dispatches little XML messages
   to and from an XML Switch.  The switch directs the XML message
   to the right resource, submits the message to the handler, and
   then returns the XML back to this web site.  The XML can then
   be displayed to you as profile information and speical offers.

<P>
  <h1>Welcome</h1>
  <form name=login action="/cgi-bin/sp.py"
        method="GET">
    Please enter a valid Customer ID* <br>
    <input type="text" size="15" name="id">&nbsp;
    <input type=submit value=" login ">
    <input type="hidden" name="mode" value="login">
    </form>
</P>
<p>*this ID must exist in your Profiles database!</p>
</BODY>
</HTML>

It’s important to point out that in Example 10-14 the form features a hidden variable named mode, which is set to login. This is used as the primary indicator for the CGI script.

<input type="hidden" name="mode" value="login">

The CGI script encounters a mode of login, and executes the correct code accordingly.

The file intro.html and its stylesheet are of no value without somewhere to send them. The login box only takes an ID, but obviously doesn’t do anything. The CGI script, on the other hand, takes the ID and runs with it.

The CGI Functionality

The CGI for this web application is sp.py, shown in Example 10-15, later in this chapter. The script has four primary purposes. The first is to take your customer ID and use it to extract your profile information from somewhere behind the XML Switch. The second purpose is to allow you to edit your profile information on the same page that it’s presented on. The third purpose is to accept your update form and send the correct message back to the Switch. The fourth purpose is to display all offers currently in the OfferXMLStore. Of course, before any of this can begin, the script needs to import its dependencies. As mentioned earlier, the script needs access to both the XML Switch client (xsc) and the XMLMessage class. The script also imports some standard XML functionality:

import os
import cgi

from xsc                     import xsc
from XMLMessage              import XMLMessage
from xml.dom.ext.reader.Sax2 import FromXml

Before the server can do any of its four purposes, it must also do some common housecleaning, such as extracting the right data from the query string and figuring out which method it needs to call:

# ''''''''''''''''''''''''''''''''''''''''''''
# MAIN
#
qs   = cgi.parse_qs(os.environ["QUERY_STRING"])
mode = qs.get("mode", "")
id   = qs.get("id", "")

print "Content-type: text/html"
print ""
print "<html>"
print """<HEAD><TITLE>SuperUltraMegaCorp Shopping Portal (intro.html)
          </TITLE></HEAD><BODY>"""
print """<P align="center"><h1>SuperUltraMegaCorp Shopping Portal</h1></P>"""
print """<p><h3>click <a href='/intro.html'>here</a> to login</h3>"""
print """<h3>click <a href='/cgi-bin/sp.py?mode=getOffers'>here</a> to 
         review offers</h3></p>"""

if mode[0] == "updateProf":
  doUpdateProfile(  )

elif mode[0] == "login":
  doLogin(id[0])

elif mode[0] == "getOffers":
  doGetOffers(  )

print "</body></html>"

As you can see, most of the work is done by three methods, doUpdateProf, doLogin, and doGetOffers. Let’s examine each of these methods in greater detail, as that is where the XML action is.

Extracting profile information

When you hit the login button, the intro.html web form sends your user ID and mode to the server. As shown earlier, the script calls a doLogin method right off the bat when it encounters a login mode. The login method accepts an ID as a parameter and builds the appropriate getProfile XML message:

def doLogin(id):
  """
   doLogin(id) - this method grabs a user's profile
   and displays it as XML on the browser, and also
   provides a form that allows the user to edit their
   own data.
  """
  # Bring up XML Switch client
  xs = xsc(  )
  xs.server = "centauri:2112"

  # prepare an XML message for the server
  xmlmsg = XMLMessage(  )

  # format a getProfile message
  xmlmsg.setXMLMessage("""
       <message>
         <header><rpc/></header>
         <body>
           <object class="CustomerProfile"
             method="getProfile">
             <param>""" + id + """</param>
           </object>
         </body>
       </message>
                       """)

As the preceding code shows, the first thing that occurs is the instantiation of the XML Switch client, as well as assignment of the xsc.server property to centauri:2112. This means we’re running the XML Switch at a host named centauri, on port 2112. After instantiating the XML Switch client, a message is prepared as shown in the previous code. This message has the current user’s ID placed within it, so the server presents the correct information.

What follows is the submission of your getProfile request, using the xsc class instantiated as xs:

# make the call to the XML Switch
xmlresp = xs.sendMessage(xmlmsg.getXMLMessage(  ))

# display response
print "<P>Response received on your behalf:<br><xmp>"
print xmlresp
print "</xmp></p>"

# setup profile XML Message and retrieve response DOM
xmlmsg.setXMLMessage(xmlresp)
msgdom = xmlmsg.getXMLMessageDom(  )

After the doLogin method has correctly formatted your message and sent it off to the server with a call to xs.sendMessage, it’s time to parse the result.

CustProfElement = msgdom.getElementsByTagName("CustomerProfile")[0]
CustProfElement.normalize(  )

# pick out ID number
id = CustProfElement.getAttributeNS('', "id")
print "<tr>"
print " <td colspan=2><b>Greetings " + id + "</b></td>"
print "</tr>"

# iterate through children, creating pre-populated
# form as we go...
nodes = CustProfElement.childNodes
for node in nodes:
  if node.nodeType == node.ELEMENT_NODE:
    print "<tr><td>"
    print "<b>" + node.nodeName + "</b>"
    print "</td><td>"
    for ec in node.childNodes:
      print "<input type=text size=20 name='" + node.nodeName  + "' "
      print " value='" + ec.nodeValue + "'>"
          
    print "</td></tr>"

At this point, a new form is created allowing you to update and change fields within your profile. Figure 10-6 shows the prepopulated form sitting in the browser.

The profile-editing form
Figure 10-6. The profile-editing form

If you hit Submit, you would trigger a different event on the server sp.py script. Also, your mode changes to updateProf, and a different call is made to the XML Switch. Your ID and mode are ready and waiting for the next form submission via hidden fields in the form markup:

  print "<input type='submit' value='update profile'>"
  print "<input type='hidden' name='id' value='" + id + "'>"
  print "<input type='hidden' name='mode' value='updateProf'>"

Although the hidden input fields don’t show up in the browser, they are still submitted during a GET or POST operation.

Updating profile information

When you submit your profile-editing form, the same sp.py is called again, but this time your mode has been flipped to updateProf. As you saw earlier, depending on the mode, a corresponding function is called. In this case, it’s the doUpdateProfile function.

To create the necessary XML message, the first thing the script does is attempt to extract the form values out of the query string and create valid XML message:

  xmlmsg =  "<message><header><rpc/></header>
"
  xmlmsg += "<body><object class='CustomerProfile' method='updateProfile'>
"
  xmlmsg += "<param><CustomerProfile id="
  xmlmsg += "'" + id[0] + "'>
"
  xmlmsg += "<firstname>" + qs.get("firstname","")[0].strip(  ) + "</firstname>
"
  xmlmsg += "<lastname>"  + qs.get("lastname","")[0].strip(  )  + "</lastname>
"
  xmlmsg += "<address1>"  + qs.get("address1","")[0].strip(  )  + "</address1>
"
  xmlmsg += "<address2>"  + qs.get("address2","")[0].strip(  )  + "</address2>
"
  xmlmsg += "<city>"      + qs.get("city","")[0].strip(  )      + "</city>
"
  xmlmsg += "<state>"     + qs.get("state","")[0].strip(  )     +"</state>
"
  xmlmsg += "<zip>"       + qs.get("zip","")[0].strip(  )       +"</zip>
"
  xmlmsg += "</CustomerProfile></param></object></body></message>"
  print xmlmsg
  print "</xmp></p>"

In this code, each required field of an updateProfile XML request is populated with something from the web form that was just submitted. With the XML message freshly created, the doUpdateProfile method moves on to actually submit the message to the XML Switch:

xs = xsc(  )
xs.server = "centauri:2112"
resp = xs.sendMessage(xmlmsg)
print "<P>Response received:<br><xmp>"
print resp
print "</xmp></p>"

print "<h1>Log Back In?</h1>"
print "<form name=login action='/cgi-bin/sp.py' method='GET'>"
print "Use Current Customer ID:<br>"
print "<input type='text' size='15' name='id'"
print " value='" + id[0] + "'>&nbsp;"
print "<input type=submit value=' login '>"
print "<input type='hidden' name='mode' value='login'>"
print "</form>"

In addition to submitting the request and displaying the confirmation, the script also creates a new HTML form with your ID preloaded, allowing you to log back in and see your modified information.

Displaying all offers

The code to display offers is some hairy XML, but is not the most difficult if you’ve been following along thus far. At first, an XML message must be prepared that the CGI script will send off to the XML Switch:

xs = xsc(  )
xs.server = "centauri:2112"

xmlmsg =  str("""<message>
                   <header><rpc/></header>
                     <body>
                       <object class="XMLOffer"
                        method="getAllOffers">
                       </object>
                     </body>
                     </message>""")
resp = xs.sendMessage(xmlmsg)

As you can see, as soon as it is prepared it is immediately submitted. If everything went okay, the doGetOffers method attempts to display all valid offers:

offdom = FromXml(resp)
offers = offdom.getElementsByTagName("offer")
  
for offer in offers:
  offer.normalize(  )
  for node in offer.childNodes:
    if (node.nodeType == node.ELEMENT_NODE):
      if (node.nodeName == "heading"):
        print "<h3>" + node.firstChild.nodeValue + "</h3>"
      if (node.nodeName == "description"):
        print "<p>" + node.firstChild.nodeValue + "</p>"

Here you use some familiar methods and operations from the DOM. In this case, a node is traversed for its heading and description nodes. Once they are found, they are descended and their text extracted.

The Complete sp.py Listing

Example 10-15 shows the complete listing of sp.py.

Example 10-15. sp.py
"""
 sp.py - a series of web pages and forms that send messages
  to the XML Switch for data access.
"""
import os
import cgi

from xsc                     import xsc
from XMLMessage              import XMLMessage
from xml.dom.ext.reader.Sax2 import FromXml

def doLogin(id):
  """
   doLogin(id) - this method grabs a user's profile
   and displays it as XML on the browser, and also
   provides a form that allows the user to edit their
   own data.
  """
  # Bring up XML Switch client
  xs = xsc(  )
  xs.server = "centauri:2112"

  # prepare an XML message for the server
  xmlmsg = XMLMessage(  )

  # format a getProfile message
  xmlmsg.setXMLMessage("""
       <message>
         <header><rpc/></header>
         <body>
           <object class="CustomerProfile"
             method="getProfile">
             <param>""" + id + """</param>
           </object>
         </body>
       </message>
                       """)

  # Display how CGI is contacting XML switch
  print "<p>Message to be sent on your behalf:<br><xmp>"
  print xmlmsg.getXMLMessage(  )
  print "</xmp></p>"

  # make the call to the XML Switch
  xmlresp = xs.sendMessage(xmlmsg.getXMLMessage(  ))

  # display response
  print "<P>Response received on your behalf:<br><xmp>"
  print xmlresp
  print "</xmp></p>"

  # setup profile XML Message and retrieve response DOM
  xmlmsg.setXMLMessage(xmlresp)
  msgdom = xmlmsg.getXMLMessageDom(  )

  # Create a form to edit this new profile data
  print "<h1>Edit your profile</h1>"
  print "<form action='sp.py' method='GET'>"
  print "<table width=450 border=1>"

  # parse result
  try:
    CustProfElement = msgdom.getElementsByTagName("CustomerProfile")[0]
    CustProfElement.normalize(  )

    # pick out ID number
    id = CustProfElement.getAttributeNS(None, "id")
    print "<tr>"
    print " <td colspan=2><b>Greetings " + id + "</b></td>"
    print "</tr>"

    # iterate through children, creating pre-populated
    # form as we go...
    nodes = CustProfElement.childNodes
    for node in nodes:
      if (node.nodeType == node.ELEMENT_NODE):
        print "<tr><td>"
        print "<b>" + node.nodeName + "</b>"
        print "</td><td>"
        for ec in node.childNodes:
          print "<input type=text size=20 name='" + node.nodeName  + "' "
          print " value='" + ec.nodeValue + "'>"
        print "</td></tr>"
  except:
     print "<tr><td>Exception</td><td>Encountered</td></tr>"

  # finish up the form
  print "<tr><td colspan=2>"
  print "<input type=submit value='update profile'>"
  print "<input type=hidden name='id' value='" + id + "'>"
  print "<input type=hidden name='mode' value='updateProf'>"
  print "</td></tr>"
  print "</table>"
  print "</form>"

def doUpdateProfile(  ):
  """
   doUpdateProfile(  ) - this method is called to update a profile
   using the updateProfile XML message.
  """
  print "<p>Update message on your behalf:<br><xmp>"

  xmlmsg =  "<message><header><rpc/></header>
"
  xmlmsg += "<body><object class='CustomerProfile' method='updateProfile'>
"
  xmlmsg += "<param><CustomerProfile id="
  xmlmsg += "'" + id[0] + "'>
"
  xmlmsg += "<firstname>" + qs.get("firstname","")[0].strip(  ) + "</firstname>
"
  xmlmsg += "<lastname>"  + qs.get("lastname","")[0].strip(  )  + "</lastname>
"
  xmlmsg += "<address1>"  + qs.get("address1","")[0].strip(  )  + "</address1>
"
  xmlmsg += "<address2>"  + qs.get("address2","")[0].strip(  )  + "</address2>
"
  xmlmsg += "<city>"      + qs.get("city","")[0].strip(  )      + "</city>
"
  xmlmsg += "<state>"     + qs.get("state","")[0].strip(  )     + "</state>
"
  xmlmsg += "<zip>"       + qs.get("zip","")[0].strip(  )       + "</zip>
"
  xmlmsg += "</CustomerProfile></param></object></body></message>"
  print xmlmsg
  print "</xmp></p>"

  print "Update in progress..."

  xs = xsc(  )
  xs.server = "centauri:2112"
  resp = xs.sendMessage(xmlmsg)
  print "<P>Response received:<br><xmp>"
  print resp
  print "</xmp></p>"

  print "<h1>Log Back In?</h1>"
  print "<form name=login action='/cgi-bin/sp.py' method='GET'>"
  print "Use Current Customer ID:<br>"
  print "<input type='text' size='15' name='id'"
  print " value='" + id[0] + "'>&nbsp;"
  print "<input type=submit value=' login '>"
  print "<input type='hidden' name='mode' value='login'>"
  print "</form>"

def doGetOffers(  ):
  print "<p>Retrieving all offers...</p>"
  xs = xsc(  )
  xs.server = "centauri:2112"

  xmlmsg =  str("""<message>
                     <header><rpc/></header>
                       <body>
                         <object class="XMLOffer"
                          method="getAllOffers">
                         </object>
                       </body>
                     </message>""")
  resp = xs.sendMessage(xmlmsg)

  print "<p>Fromatting response from server..."
  print "<h1>Offers!</h1>"

  offdom = FromXml(resp)
  offers = offdom.getElementsByTagName("offer")

  for offer in offers:
    offer.normalize(  )
    for node in offer.childNodes:
      if (node.nodeType == node.ELEMENT_NODE):
        if (node.nodeName == "heading"):
          print "<h3>" + node.firstChild.nodeValue + "</h3>"
        if (node.nodeName == "description"):
          print "<p>" + node.firstChild.nodeValue + "</p>"

# ''''''''''''''''''''''''''''''''''''''''''''
# MAIN
#
qs   = cgi.parse_qs(os.environ["QUERY_STRING"])
mode = qs.get("mode", "")
id   = qs.get("id", "")

print "Content-type: text/html"
print ""
print "<html>"
print """<HEAD><TITLE>SuperUltraMegaCorp Shopping Portal (intro.html)
          </TITLE></HEAD><BODY>"""
print """<P align="center"><h1>SuperUltraMegaCorp Shopping Portal</h1></P>"""
print """<p><h3>click <a href='/intro.html'>here</a> to login</h3>"""
print """   <h3>click <a href='/cgi-bin/sp.py?mode=getOffers'>here</a> to review offers
</h3></p>"""

if mode[0] == "updateProf":
  doUpdateProfile(  )

if mode[0] == "login":
  doLogin(id[0])

if mode[0] == "getOffers":
  doGetOffers(  )

print "</body></html>"

Running the Site as a User

Once you have completed all of the prerequisites for this mammoth web site and XML integration, you’ll likely want to test it with a browser. There are eight steps to get things going:

  1. Make sure your web server is running, and that intro.html and sp.py are functioning properly.

  2. Make sure your XML Switch is running (runxs.py).

  3. Make sure your web forms point to your web server and that your xsc calls point to your XML Switch server.

  4. Browse to intro.html and place a valid ID from your database in the text box. Click Login.

  5. You can review your profile, and edit it in the form at the bottom of the page.

  6. If you’ve changed some items, click the Submit button on the new form to login again.

  7. Verify that your data has changed, then click on the link to check your offers.

  8. The offers should be presented as formatted HTML.

The Web/CGI application presented here is the tip of an iceberg of integration. The browser talks to the web site. The web site talks to the XML Switch. The XML Switch uses local objects to retrieve information from relational databases and flat files. By standardizing on XML messages between distributed systems and creating an intermediary routing capability between these applications, a much greater level of integration is afforded, with a greater capacity for scaling.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.116.67.70