end-users. Backend systems of different types are integrated by exposing XML
messaging APIs, allowing the portal to work. Other applications connect along the
peripheral boundary of the main processing, to participate in the flow of XML
10.1.1 Decoupling Application Systems
Take for instance the Web/CGI application on the righthand side of Figure 10-1
Typically, it is connected directly to a database that contains the information it needs.
However, in this scenario, the database is decoupled from the Web/CGI application
by the XML switch. The Web/CGI application only submits an XML packet (a SOAP-
like packet in this chapter) asking for relevant data. The switch interprets this packet
and forwards it to the right location (in this particular case, a Python class that can
perform the appropriate work). When the database responds, the information is
"routed" back to the original caller. This allows for flexibility in the location or design
of the database. In fact, it lets distributed systems commit to XML interfaces as they
expose their functionality, instead of committing to the intricacies of table design or
specific stored-procedure names.
10.1.2 Routing Adds Flexibility
This type of design is similar in architecture to message queuing designs, with some
subtle and powerful differences. By decoupling different pieces of an enterprise,
you're able to share their functionality more broadly, and enjoy the independence of
not being tightly bound to any specific application that consumes the data. In
addition, by placing an intermediary between data sources and data consumers, you
allow for business logic to exist at a level above and between applications, and not
programmed directly into them. For example, in Figure 10-1
, you could conceivably
swap out the Profiles database, and instead use a third-party service on the Internet
that contains customer data. That is, if you find that a web service provides better
personalization and consumer data than you, you can connect with the service,
develop an XML interface that is familiar to your network, and swap out the old
database for the new service partner. The local applications as consumers of the
information would be none the wiser, as the XML switch is still accepting their XML
requests and results are still being routed back to the applications.
10.1.3 Routing Adds Scalability
In addition to flexibility in system components, routing also adds another degree of
scalability to a system. By routing XML requests for information around your network,
you enable the ability to manage them as you would other types of network traffic.
For example, you can replace individual systems with system farms consisting of
multiple servers, and load balance the XML packets that are sent between them. In
another case, XML switches could be daisy-chained together, much like routers and
hubs are chained together for TCP/IP traffic. This concept enables you to do things
such as broadcasting and duplication of read-only signals. It also allows you to
create redundancy and multiple network paths to the same location.
10.2 Understanding the Scope
The sample application is the most ambitious example presented in this book yet. As
such, it's a good idea to create a checklist and breakdown of what is involved and
what dependencies exist.
This application can be created on one machine, but that machine needs to have all
the different components installed. Ideally, two machines are a better fit. Regardless,
the five pieces involved are as follows:
• A simple relational database that hosts one simple table. I used SQL
Server, but MySQL or Postgres should work just fine.
• Data Access Object. This native Python class will encapsulate the data held
within the SQL database behind an XML-friendly interface.
• An XML data store. This is a flat file that you create, coupled with an object
to access the data using XML for both input and output.
• The XML Switch. The XML Switch itself is a Python-based interpretive XML-
routing and object-brokering mechanism. If loaded with a routing table, it
could analyze an incoming XML SOAP packet with XPath and determine which
destination it should travel to. It could send a SOAP packet anywhere,
including to web servers. In this example, the switch uses XML messages as
RPC calls into the objects it hosts.
• Web/CGI Application. This a Python-based CGI script and accompanying
web page similar to the CGI scripts created earlier in this book. It should run
on any web server (IIS, Apache) that has been configured for CGI.
The code used in these examples is intended as a starting point for educational
purposes. Understand that it does not have the necessary error handling or
robustness to be used directly in applications. This chapter should be considered a
design exercise showing a collection of different techniques for doing a whole scope
of the things required in integrating distributed systems with XML.
10.3 Building the Database
Explaining the installation of a specific database is outside the scope of this book.
However, if you are completely new to databases, we provide a quick overview of
how this particular database was set up and used in this example. This overview
broadly applies to any database systems, including SQL Server, Postgres, and MySQL.
As a Python XML developer gluing together applications, understanding at least the
fundamentals of working with databases will serve you well.
We used Microsoft SQL Server during the creation of this example as our relational
database. However, only one simple table is created, and any database that supports
SQL queries should be fine. A SQL table creation script is provided that should work
on just about any SQL platform. However, the Python connectivity code presented
here uses ODBC for access. If you choose to use a different database than SQL
Server, you may need to download a Python API to access it. For example, the
MySQLdb API is available at http://sourceforge.net/projects/mysql-python/
provide access from Python to MySQL databases. Regardless of your connectivity API,
the SQL calls shown in this chapter should be identical.
10.3.1 Creating a Profiles Database
If you've installed your database of choice, your first task is to create a database
inside of the system. If you are using SQL Server or your database offers an
administrative GUI, this process may be as easy as typing a database name into a
dialog box. For example, if using SQL Server, just browse to the databases folder
using SQL Enterprise Manager. Once there, right-click and choose New Database.
The name of the database should be Profiles. If you don't have a GUI, a SQL
statement as simple as the following should suffice:
CREATE DATABASE Profiles
Once created, you may want to enable an account that has read and write privileges
to this database, but to no others. Consult your database's documentation for details
on creating specific user accounts. For the purposes of this example, in SQL Server
the user webuser has been created, with a password of w3bus3r. The authentication
information is required in the ODBC connectivity code.
10.3.2 Creating a Customer Table
Once you've created a database using either a GUI or SQL statements, create one
simple table named Customer. This table represents some basic user information. It
will be used by the different distributed applications as the one and only customer
information record. While the fields in this table only cover the basics, you could
easily expand them with other types of information related to the system.
The Customer table can be created with a GUI in SQL Enterprise Manager for SQL
Server, or with the following SQL in any database:
CREATE TABLE Customer (
firstname varchar (255) NULL,
lastname varchar (255) NULL,
address1 varchar (255) NULL,
address2 varchar (255) NULL,
city varchar (255) NULL,
state varchar (2) NULL,
zip varchar (10) NULL,
customerId varchar (40) NULL)
The table is very simple. All of the data types are varchar and can easily be handled
in Python as strings and integers. One thing to note about the Customer table is the
varying length of the different fields. For example, most of the customer information
may be zero to 256 characters in length. However, others in the table must conform
to constraints such as two characters for a state abbreviation, and a 10-digit
requirement on the zip code.
If you are using SQL Server, remember to expose your new database as an ODBC
source on the machine you're running any database clients on—in this example
application, only the XML Switch, which loads the CustomerProfile class, needs
database connectivity. To enable connectivity to SQL Server, use the ODBC manager
in the Windows' Control Panel to choose your database. Once this step is completed,
the ODBC code presented here will work.
10.3.3 Populating the Database
You can populate the fields in your new table with an SQL statement similar to the
insert into Customer values('John',
'123 Evergreen Terrace',
This statement creates a new row in the database table with the corresponding
values contained in quotes. If you want to fill your database with several rows, you
can resort to good, old-fashioned data entry with the popdb.py script shown in
. This simple script just reads input from the command line and inserts
it into the database. It's designed for use with the ODBC module and SQL Server, so
if using another database, you need to adapt the connectivity code.
Example 10-1. popdb.py
popdb.py - populate the Profiles/Customer DB with ODBC calls
import dbi, odbc
conn = odbc.odbc("Profiles/webuser/w3bus3r")
cmd = conn.cursor( )
# loop to get input values.
firstname = raw_input("firstname:")
lastname = raw_input("lastname:")
address1 = raw_input("address1:")
address2 = raw_input("address2:")
city = raw_input("city:")
state = raw_input("state, 2 letter max:")
zip = raw_input("zip, 10 digit max:")
customerId = raw_input("Customer ID, 40 character max length:")
# execute SQL statement
cmd.execute("insert into Customer values('"
+ firstname + "', '"
+ lastname + "', '"
+ address1 + "', '"
+ address2 + "', '"
+ city + "', '"
+ state + "', '"
+ zip + "', '"
+ customerId + "')")
# ask for additional entries
finished = raw_input("another? [y/n]:")
if (finished == "n"):
There is no error checking in popdb.py, so if you violate one of the table constraints,
you get an exception, and that particular row won't be inserted.
10.4 Building the Profiles Access Class
In addition to the Profiles database is the CustomerProfile Python class. This
object performs XML input and output against the database, and exposes methods
for use by the XML Switch. The basic profile actions allow you to retrieve, insert,
update, and delete profiles. Arguments to these methods are XML versions of profile
data. By making the customer profile packets in XML, it's easy for other applications
to generate and consume the packets without any concern for the structure of the
database, or even how to access it directly. In fact, a CustomerProfile can easily
become the payload of a SOAP message. A profile packet appears as:
<address1>396 Evergreen Terrace</address1>
Note that the address2 element exists, even though it is empty. The DTD for such a
document appears as:
<!ELEMENT firstname (#PCDATA)>
<!ELEMENT lastname (#PCDATA)>
<!ELEMENT address1 (#PCDATA)>
<!ELEMENT address2 (#PCDATA)>
<!ELEMENT city (#PCDATA)>
<!ELEMENT state (#PCDATA)>
<!ELEMENT zip (#PCDATA)>
id CDATA #REQUIRED>
city, state, zip)>
An instance of the document using the DTD needs to have the declaration within the
document as well:
<!DOCTYPE CustomerProfile SYSTEM "CustomerProfile.dtd">
In order to keep things within the scope of this chapter, DTD enforcement is not a
part of the CustomerProfile Python class, although the DTD rides along with the
document and may be utilized at a later date. When embedding CustomerProfile
elements within an XML message, the prolog is stripped out, and only the
CustomerProfile element is inserted into the XML message.
10.4.1 The Interfaces
The CustomerProfile class supports four distinct operations. These operations allow
for retrieval, insertion, updates, and deletes. This class is used by the XML switch to
manage the insertion and retrieval of CustomerProfile information at runtime in the
distributed system. All communication to and from this class takes the form of XML—
this enables greater flexibility in how the data is stored on the backend. This also
alleviates the burden of requiring distributed applications to connect directly to the
database and understand the structure of its tables. In this scenario, distributed
applications only need to understand structure of a CustomerProfile document.
This method accepts a customer profile ID and returns the corresponding
information in a well-formed, valid CustomerProfile document in the form of
This method is identical to getProfile, except that the return value is not a
string of XML, but rather a DOM instance. The DOM can then be used for
The insertProfile method takes a valid, well-formed CustomerProfile
document as a string and inserts it into the database.
Similar to insertProfile, this method takes a fresh XML CustomerProfile
chunk and updates the existing record in the database based on the customer
ID. Under the covers, it performs a delete and insert respectively.
This method takes a customer ID as a parameter, and deletes the
corresponding record from the database.
With the exception of the getProfile and getProfileAsDom methods, these
methods return either 1 or 0 (true or false) to the caller, enabling them to be used
as arguments to if statements.
10.4.2 Getting Profiles
The CustomerProfile class for retrieving profiles exposes two methods: getProfile
and getProfileAsDom. Both methods take a customerId as an argument. In a
simple test case, you could use the methods as follows:
from CustomerProfile import CustomerProfile
from xml.dom.ext import PrettyPrint
cp = CustomerProfile( )
print "String of XML:"
print "Or retrieve a DOM:"
dom = cp.getProfileAsDom("234-E838839")
This assumes that you have populated a record in the database with a customerId of
234-E838839. The result of running this code is the output of two identical XML
String of XML:
<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE CustomerProfile SYSTEM "CustomerProfile.dtd">
Documents you may be interested
Documents you may be interested