Wiley - Oracle XSQL - Combining SQL, Oracle Text, XSLT and Java To Publish Dynamic Web Content - 2003 PDF
Wiley - Oracle XSQL - Combining SQL, Oracle Text, XSLT and Java To Publish Dynamic Web Content - 2003 PDF
Michael D. Thomas
Publisher: Robert Ipsen
Editor: Theresa Hudson
Developmental Editor: Kathryn A. Malm
Managing Editor: Micheline Frederick
Text Design & Composition: Wiley Composition Services
This book is printed on acid-free paper. ∞
Copyright © 2003 by Michael D. Thomas. All rights reserved.
Published by Wiley Publishing, Inc., Indianapolis, Indiana
Published simultaneously in Canada
No part of this publication may be reproduced, stored in a retrieval system, or transmitted
in any form or by any means, electronic, mechanical, photocopying, recording, scanning, or
otherwise, except as permitted under Section 107 or 108 of the 1976 United States Copyright
Act, without either the prior written permission of the Publisher, or authorization through
payment of the appropriate per-copy fee to the Copyright Clearance Center, Inc., 222 Rose-
wood Drive, Danvers, MA 01923, (978) 750-8400, fax (978) 750-4470. Requests to the Pub-
lisher for permission should be addressed to the Legal Department, Wiley Publishing, Inc.,
10475 Crosspointe Blvd., Indianapolis, IN 46256, (317) 572-3447, fax (317) 572-4447, E-mail:
permcoordinator@wiley.com.
Limit of Liability/Disclaimer of Warranty: While the publisher and author have used their
best efforts in preparing this book, they make no representations or warranties with respect
to the accuracy or completeness of the contents of this book and specifically disclaim any
implied warranties of merchantability or fitness for a particular purpose. No warranty may
be created or extended by sales representatives or written sales materials. The advice and
strategies contained herein may not be suitable for your situation. You should consult with
a professional where appropriate. Neither the publisher nor author shall be liable for any
loss of profit or any other commercial damages, including but not limited to special, inci-
dental, consequential, or other damages.
For general information on our other products and services please contact our Customer
Care Department within the United States at (800) 762-2974, outside the United States at
(317) 572-3993 or fax (317) 572-4002.
Trademarks: Wiley, the Wiley Pubishing logo and related trade dress are trademarks or reg-
istered trademarks of Wiley Publishing, Inc., in the United States and other countries, and
may not be used without written permission. All other trademarks are the property of their
respective owners. Wiley Publishing, Inc., is not associated with any product or vendor
mentioned in this book.
Wiley also publishes its books in a variety of electronic formats. Some content that appears
in print may not be available in electronic books.
ISBN 0-471-27120-9
Printed in the United States of America
10 9 8 7 6 5 4 3 2 1
To my wife, Aylett—your smile brightens my days.
Contents
Sign 196
Sqrt 197
Trunc 197
Logarithmic and Trigonometric Functions 197
Character Functions 198
Ascii 198
Bitand 199
Chr 200
Concat 200
Greatest 200
Initcap 201
Instr 201
Instrb 202
Least 203
Length 203
Lengthb 204
Lower 204
Lpad 204
Ltrim 205
Nls_initcap 205
Nls_lower 206
Nls_upper 206
Nls_sort 206
Replace 206
Rpad 207
Rtrim 207
Soundex 208
Substr 208
Substrb 209
Translate 209
Trim 210
Upper 210
Date Functions 211
Add_months 211
Current_timestamp 211
Dbtimezone 212
Extract 212
Greatest 213
Last_day 213
Least 214
Local_timestamp 214
Months_between 214
New_time 215
Next_day 215
Numtodsinterval 216
Numtoyminterval 217
Round 217
Contents xiii
Sessiontimezone 218
Sys_extract_utc 218
Sysdate 218
Systimestamp 219
Trunc 219
Conversion Functions 219
Convert 221
Decompose 222
Translate 222
Unistr 222
Miscellaneous Functions 223
Moving On 224
Chapter 9 PL/SQL 225
Hello, PL/SQL! 225
Structure 228
Declaring Variables 228
Scalar Variable Declarations 229
Record Declarations 231
Cursors 232
Array Structures 232
PL/SQL Blocks and the Execution Section 234
SQL Statements in PL/SQL 235
Control Structures 237
Conditional Control Structures 237
Iterative Control Structures 239
Sequential Control Structures 240
Cursors 241
Packages 244
Procedures and Functions 245
Exceptions 247
Triggers 248
Reference Cursors 250
PL/SQL and XSQL 251
Moving On 251
Chapter 10 Using Oracle Text 253
Simple Keyword Searching 254
Stemming, Fuzzy, Wildcard, and Soundex Searching 255
Searching within XML Documents 256
Other Features 259
Moving On 259
Chapter 11 Retrieving XML 261
What’s So Hard? 261
Creating XML with xsql:include-owa 263
Generating XML from PL/SQL 265
XMLType 267
Moving On 271
xiv Contents
local-name 358
Name 358
namespace-uri 358
position 358
String Functions 359
concat 359
contains 359
normalize-space 359
starts-with 359
string 360
string-length 360
Substring 361
substring-before 361
substring-after 362
translate 362
boolean Functions 363
boolean 363
false 364
lang 364
not 364
true 364
Number Functions 364
ceiling 364
floor 364
number 365
round 365
sum 365
Moving On 366
Chapter 14 Building XSQL Web Applications 367
Application Architecture 368
The Simple XSQL Architecture 369
Process for Developing Your Architecture 370
Extension Options 375
Stored Procedures 375
Action Handlers 376
Programmatic Invocation 377
Stylesheet Extensions 378
Extending the Interface with JavaScript and CSS 378
Extension at the Database Level 379
A Sample Application 380
The Requirements 380
Application Interface Design 381
Database Requirements 385
Database Design 387
Database Implementation 391
Writing the SQL 392
Contents xvii
xix
CHAPTER
Welcome to the exciting world of eXtended Structured Query Language (XSQL) devel-
opment! What’s so exciting? Efficiency and ease of use. XSQL isn’t some razzle-dazzle
technology to wow your users. It also isn’t the latest X standard du jour that no one can
stop talking about—until you ask, “But what does it do for me today?” The problem
with all of the great stuff out there is that no one technology does it all. A Web server
doesn’t store all of the company’s inventory data. A database, by itself, cannot present
its data to its users in an attractive and usable manner. This is where XSQL comes in.
XSQL allows you to easily leverage the most robust, mature, and usable technologies
in the industry: Standard Query Language (SQL), HyperText Markup Language
(HTML), HyperText Transfer Protocol (HTTP), eXtensible Markup Language (XML),
Java, and the Oracle relational database management system (RDBMS).
Each of these technologies is arguably the best-of-breed in its space. When it comes
to querying relational data, SQL has no competitors. HTML and HTTP are the wonder
twins of the Internet. They have their faults, but they also have the ability to evolve.
Java has had unparalleled success in the world of enterprise applications and will con-
tinue to do so. XML is the standard for structuring data in a platform and application-
independent manner. Last but not least, the Oracle RDBMS is the technology, as well as
the market, leader in its space.
In the next few hundred pages, XSQL allows you to bring these powerful pieces
together. You’ll learn how you can use XSQL to instantly present your database data on
the Web. You’ll develop a complete application with just XSQL and eXtensible
1
2 Chapter 1
Stylesheet Language Transformation (XSLT). You’ll also see how to use XSQL to create
graphics on the fly and to render Portable Document Format (PDF) documents based
on dynamic data. All the while, the easiest cases require no Java coding at all. You only
have to use Java coding for the more complex interactions with the database.
This chapter serves as a general overview to XSQL, as well as the foundation tech-
nologies. The first topic covered is an examination of what XSQL solves. This includes
some short code examples that illustrate how powerful XSQL can be. The next discus-
sion explores how XSQL relates to other Oracle technologies. You don’t have to use
XSQL exclusively with Oracle, but XSQL makes a great combination. The chapter ends
with some in-depth exploration of XML. XSQL and XSLT are derived from XML, so
you need to have a good understanding of XML before diving in.
From this concept, the hyperlink as we know it today was born. Now, Tim Berners-
Lee wasn’t the first to conceive of a hyperlink, but he implemented his system correctly
and kept it simple enough so that it could propagate. He was also helped by a couple
of other factors. First, the timing was right. The Internet’s underlying protocol, Trans-
mission Control Protocol/Internet Protocol (TCP/IP), was well formed and widely
used by this time. Second, he invented the system in an academic setting. This is a
commonality of many of the great Internet standards. It’s easier to freely share a set
of protocols through academia than in a commercial landscape. In the early 1990s,
it was unheard of for software companies to give away complex software and designs
for free.
However, the key reason why the Web grew is that it began as a very simple system.
HTTP began as an extremely simple protocol and remains largely so to this day. You
send a request and receive a response. It’s the server’s duty to figure out how to
respond, and it’s the client’s duty to act upon the response. There were only three pos-
sible methods for asking: GET, POST, and the rarely used HEAD. This made it easy to
develop Web servers. The client only had to understand a handful of possible
responses. Likewise, HTML was designed with simplicity in mind. Instead of using the
much more powerful but much more complex SGML, Berners-Lee opted for only a
subset. Developing servers and clients was so easy, in fact, that much of the early
development of the Web was completed by developers and students working in their
spare time, or by professionals working on it as a special project. It’s telling to note that
the two institutions that did so much to give birth to the Web—the Conseil Européen
pour la Recherche Nucléaire (CERN) and the National Center for Supercomputing
Applications at the University of Illinois at Urbana-Champaign—had no research
focus on network computing at the time the Web was invented in their labs!
Because of its simplicity, the Web spread like wildfire. It spread so far that now more
than half of the American people have Web access. Most everyone who has a computer
on their desk at work has Web access. This pervasiveness makes it an ideal platform for
application development. You don’t have to worry about installing a software applica-
tion on everybody’s desktop or requiring customers to install software at home. On top
of that, you don’t have to worry about different platforms. From a logistical standpoint
alone, the Web is the obvious platform choice for many applications.
There’s only one little problem: The Web was originally intended for simple docu-
ment sharing! This causes some issues, the most obvious of which derives from the
stateless nature of the Web. HTTP originally didn’t support cookies, which allow you
to bind different HTTP transactions together into user sessions. When you are just
sharing static documents, you don’t need to tie different HTTP transactions together.
The documents always remain the same, so it doesn’t matter what documents the user
requested previously. However, when your data is dynamic, it often does matter what
requests preceded the current one. A shopping cart application is a good example of
this. When the user is ready to purchase the items, the Web application has to have
tracked what items were selected across several HTTP transactions.
There are several techniques to address this problem, not the least of which is cook-
ies. XSQL fully supports both cookies and servlet sessions. You’ll learn about these
mechanisms as the book progresses. More to the point, though: the mechanisms for
supporting sessions were added to the original HTTP after the initial design, as were
JavaScript and the concept of connecting databases to the Web. Perhaps most impor-
tant, however, is that HTML documents are inadequate for conveying information.
4 Chapter 1
HTML only gives instructions to a Web browser regarding how to display the infor-
mation. This is a very important function, but it means that you can’t interpret the
HTML semantically. This is where XML comes in.
It’s easy to say that the Web could have been designed better, but hindsight is
always 20/20. In truth, the Web is great because it’s so easy to extend. Though it was
originally intended for static documents, it was easy to add support for images and
dynamic data. A Web server doesn’t care what kind of information it sends or where it
came from. HTTP merely describes the mechanics of transferring the information in a
simple and lightweight manner. If the HTML document contains JavaScript, that’s
fine—it’s up to the browser to understand how to use that information.
Likewise, creating database-driven Web pages is just a burden on the server. Web
browsers don’t know the first thing about interacting with a database. Strictly speak-
ing, an HTTP server process doesn’t, either. It just knows to hand off certain URLs to
servlets and other server-side modules that interact with the database and produce
dynamic results.
This evolution continues today with Web services. HTTP is so simple that you can
easily embed simple Web clients in your code. Then, you can grab data from remote
machines and use their data in your programs. Because HTTP doesn’t care what is
sent, you can use XML to structure the data. HTTP is also very loose in what it receives,
so you can send data back to the server. Thus, a protocol originally intended to make it
easy for physicists to share documents can be used as the backbone for powerful dis-
tributed applications.
The process of developing Web applications is maturing. While early Web applica-
tion developers had to concoct solutions as they encountered a wide variety of prob-
lems, a lot of the pioneering is done. The best solutions are being recognized as such
and adopted widely. Java and Java Database Connectivity (JDBC) are good examples
of this, as are XML and XSLT.
The XSQL framework is yet another evolution in Web development. With XSQL,
producing dynamic Web pages that interact with the database is nearly as simple as
writing an HTML page itself. In the not-so-distant past, you had to write a module in a
language like Java, Perl, or C++ that managed the database connection, executed SQL
against the database, and then processed the results. That just got you started. From
there, you had to figure out what to do with the results. Because the number and type
of results could vary widely for the same module, you had to deal with issues like how
many results to put on a page and how to format different types. This model, often
called the three-layered model, is illustrated in Figure 1.1.
Middle Tier
Client Database
Figure 1.1 The three-layered model.
Introducing Oracle XSQL 5
As already discussed, the user interface (UI) tier only knows how to present the data
to the user. The database stores the data, usually for a variety of purposes beyond any
one particular application. This leaves a lot of work to be done by that middle layer. A
lot of architects like to refer to the middle layer as containing business logic. This
euphemism seems to imply that the middle layer is a pristine set of simple, easy rules
like “fill the warehouses to 80 percent of capacity” and “offer 10 percent discounts
across the board.” The client takes care of all the messy UI stuff, while the database
does the hard work of managing the data.
When you peel back and examine that middle layer, it usually doesn’t look like the
drawings on the whiteboard. Instead, you find a lot of hard-coded SQL and UI code
deep in the middle layer. Though many application development teams do their best
to separate a presentation layer and a database layer, it’s hard to define the lines
sharply. Even if you use a scripting language like Java Servlet Pages (JSP), the snippets
of code usually have dependencies deep in the system. What if the UI designer decides
they want a slightly different set of data returned, or they want it ordered in a different
way? Then you have to find the SQL statement and change it. That might have reper-
cussions elsewhere in the system. You might have to create a new class. Thus, to make
a relatively simple UI change, you are forced to make changes at the database layer.
When the system inevitably has to be extended, then you’ll probably find very little of
your UI code to be truly reusable.
Now, let’s assume, for a moment, that a particular application has achieved a good
separation between the various layers. Everyone read all of the design pattern books
and spent a lot of time at the whiteboard before coding started. They all had enough
time to do the separation correctly, and the management or customer understood how
important it is. There is still another problem: The output of the system is HTML. What
if you want to make the data available to Web services or to a new platform such as
wireless? Then you have to port the UI layer to an entirely new type of user interface.
Because the code was written with only HTML in mind, you’ll probably have to
rewrite all of the interface widgets. If the system is perfectly designed, this is a lot of
work. Now, imagine if the system isn’t perfectly designed.
The Web is the greatest accidental application development platform ever. It started
as an internal project at an academic institute in Switzerland and has grown into one of
the great technological forces of our time. It has its complexities and intricacies, but it
is infinitely adaptable. The key to the success of the Web is to understand it as an evolv-
ing technology. The art of developing Web applications is also evolving, and a success-
ful Web application developer is always on the lookout for the next evolution. Now, it’s
time to see how XSQL greatly simplifies the process of developing for this platform.
To see why, examine what you actually need when developing a database-driven
Web application. Clear your mind of all of the three-tier and n-tier talk that you’ve
heard. Many database-driven Web pages out there are really just beautified database
queries. For these Web pages, the requirements are simple:
■■ SQL for extracting the data
■■ A way to produce HTML for presentation
Creating the SQL is simple. If you don’t already know it, you’ll learn all about it in
Chapter 8. The problem is that you have to go from the SQL result set to the HTML
markup. In many cases, this is what the entire middle tier in the three-tier model is
doing. You start with a result set of some sort, like this:
This isn’t very palatable for the users, so you want it to look prettier. The results are
shown in Figure 1.2.
In a case like this, you aren’t doing a lot of work on the data. The data is the same,
and the results are in the same order. You just want to present the results in a better
way. All you need is a way to transform the results that come back from the database
into what you want. The ideal situation is shown in Figure 1.3.
This is where XSQL and XSLT come in. XSLT will take an XML document and transform
it into whatever you want. It is an open-standards solution to the problem of merging
dynamic data with static HTML. This usually means transforming it into HTML, but you
aren’t limited to just that. For instance, in Chapter 15, you’ll see how you can use XSLT to
write scripts based on database data. If you know cascading style sheets (CSS), then you
have a head start in understanding XSLT. If you are familiar with Server Side Includes, you
can consider XSLT as Server Side Includes on steroids. XSLT is almost always used in com-
bination with XSQL in some way. The core architecture is shown in Figure 1.4.
To get a better idea of how SQL, XSQL, and XSLT work together, here is some sam-
ple code. These two files are all you need to produce the Web page that was shown pre-
viously in Figure 1.2. The first file is the XSQL page. The <xsql:query> element,
which is called an action, defines the SQL query that you need:
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”emp-intro.xsl”?>
<page xmlns:xsql=”urn:oracle-xsql” connection=”demo”>
<xsql:query>
SELECT ename, job, sal FROM emp
WHERE deptno=20
ORDER BY sal
</xsql:query>
</page>
Client Database
SQL Query
SELECT ename, job, sal
FROM emp
WHERE deptno=20
ORDER BY sal
Template
Figure 1.3 Transforming SQL results.
8 Chapter 1
Client
XSQL Page
</xsql:query>
SELECT ename, job, sal
FROM emp
WHERE deptno=20
ORDER BY sal
</xsql:query>
1 4 Database
12
2 5
3 XSQL
HTTP Processor
XSQL Servlet
Server
11
6
10
XSLT
Stylesheet
Figure 1.4 Core XSQL architecture.
The XSQL page processor connects to the database, gets the results back, and returns
the following XML:
<page>
<ROWSET>
<ROW num=”1”>
<ENAME>SMITH</ENAME>
<JOB>CLERK</JOB>
<SAL>800</SAL>
</ROW>
<ROW num=”2”>
<ENAME>ADAMS</ENAME>
<JOB>CLERK</JOB>
<SAL>1100</SAL>
</ROW>
<ROW num=”3”>
<ENAME>JONES</ENAME>
<JOB>MANAGER</JOB>
<SAL>2975</SAL>
</ROW>
<ROW num=”4”>
<ENAME>SCOTT</ENAME>
<JOB>ANALYST</JOB>
Introducing Oracle XSQL 9
<SAL>3000</SAL>
</ROW>
<ROW num=”5”>
<ENAME>FORD</ENAME>
<JOB>ANALYST</JOB>
<SAL>3000</SAL>
</ROW>
</ROWSET>
</page>
The second line of the XSQL file links to an XSLT stylesheet. The XSQL page proces-
sor tells the XSLT processor to take the XML and transform it according to the
stylesheet. Here is the stylesheet that produces the output that you see in Figure 1.2:
<?xml version=”1.0”?>
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<html>
<head><title>Simple Stylesheet</title></head>
<body>
<h1>Employees</h1>
<table border=”1”>
<tr bgcolor=”#DDDDDD”>
<td><b>Name</b></td><td><b>Job</b></td><td><b>Salary</b></td>
</tr>
<xsl:apply-templates select=”ROWSET/ROW”/>
</table>
<hr />
</body>
</html>
</xsl:template>
<xsl:template match=”ROWSET/ROW”>
<tr>
<td>
<xsl:value-of select=”ENAME”/>
</td>
<td>
<xsl:value-of select=”JOB”/>
</td>
<td>
<xsl:value-of select=”SAL”/>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
10 Chapter 1
You’ll learn all about stylesheets in Chapter 13, but you probably want a brief
description of what is going on here. The root element of the XML document is called
page, so the page template in the stylesheet is processed first. That template is every-
thing from <xsl:template match=”page”> to the next </xsl:template> tag.
All of the static HTML in the template is written out more or less verbatim. The second
template, ROWSET/ROW, is called inside the table. It defines how the values for each
row in the result set should be displayed. As you can see from the screenshot, this
template is called for each row that was returned in the result set. If you change the
queries so more rows are returned, they will all be displayed.
If you look at the stylesheet, the majority of the code is HTML. It also falls out very
logically—the dynamic data appears amid the static HTML, precisely where you want
it to. The principle part of the XSQL page is a SQL query. At the beginning of this sec-
tion, it was stated that you only really needed a SQL statement and a way to turn the
results to HTML. The XSQL solution is very close to this. You will need to learn about
XSQL and XSLT, but notice that there has been no mention of Java, JSP, JDBC, business
logic, or anything else having to do with the middle tier. XSQL handles all of those
details for you.
This takes care of getting data from the database, but what about putting data into
it? You can also do that with XSQL. You use a different action, called <xsql:dml>.
Instead of specifying a select statement, you issue a statement that will modify the
data. As you’ll see in Chapter 14, you can use it in conjunction with forms to create edi-
tors. XSQL also provides you with built-in ways to call stored procedures.
You may be looking at this and thinking, “Simple! . . . but maybe a little too
simple . . . .” Of course, the simple architecture isn’t going to be good enough for all
problems. But that doesn’t mean that you can’t use XSQL to solve harder problems that
involve multiple queries or complex validation before putting data into the database.
Luckily, you can easily extend XSQL. Remember the <xsql:query> that you saw in
the same page? You can write your own special actions that you can use just like that
one. The action handler code that processes the action is written in Java. Your action
handler code can do whatever it likes, and you can pass to it any type of data you like
from the XSQL page. As with the <xsql:dml> action, you can also make use of para-
meters passed on by the user. The only expectation is that the action handler generate
XML to be added to the datagram. Then, a stylesheet specified in the XSQL page can be
used to convert the datagram for presentation. Figure 1.5 diagrams the XSQL architec-
ture with action handlers.
There is one final trick up XSQL’s sleeve. You aren’t limited strictly to text data! As
you’ll see in Chapter 19, you can use serializers to produce images and PDF docu-
ments. Once again, XSQL can be easily extended to conquer tough problems. The
XSQL architecture with serializers is specified in Figure 1.6. The serializer can be used
to control what is written as the final output.
XSQL makes it very easy to create simple database-driven Web pages. For the more
complex cases, you can use custom action handlers and serializers to extend the archi-
tecture. Your custom code works seamlessly with the rest of XSQL, so you don’t give
up XSQL’s elegance and simplicity—you just augment it. XSQL becomes even more
powerful when you use it in conjunction with other Oracle technologies, such as Ora-
cle Text and Oracle XML DB. You’ll read more about that in the next section.
Introducing Oracle XSQL 11
Client
Custom Action
Handler 1
Custom Action
Handler 2
some-page.xsql
com.your.custom.ActionHandler1 com.your.custom.ActionHandler2
Client
Custom Action
Handler 1
Custom Action
Handler 2
some-page.xsql
technologies. Oracle provides a rich Java application program interface (API) for data-
base access and XML. You’ll use these when extending XSQL with action handlers and
serializers, and also when using XSQL from inside programs. You can also use Oracle
JDeveloper to help you develop your XSQL pages. This section looks at the core tech-
nologies and how they relate to XSQL.
Oracle Text
SQL is great when the data is structured, like accounts receivable or inventory. A lot of
data, however, is unstructured text. Searching text is different than searching records in
a database. When searching text, you want to know if keywords occur in the text and
how they occur. SQL, on the other hand, is used mainly to see if a record matches to a
particular term. Oracle Text bridges the gap by allowing you to perform complex
unstructured text searches inside SQL statements.
Because Oracle Text is tightly integrated with Oracle SQL, you can use it from any
other SQL statement in your XSQL pages.
XML Support
XML is a fantastic way to store and transport data. However, it doesn’t exist in isolation.
If you are writing a program that consumes XML, you need to be able to parse, create,
and update XML documents. If you want to store XML, you’ll quickly run into prob-
lems if you strictly try to store XML as files. Multithreaded applications don’t interact
well with flat files. What if two users are trying to write to the file at the same time?
Oracle provides a variety of tools to help you in the brave new world of XML. Oracle
provides an XML parser and XSLT processor. These are used by the XSQL servlet in the
simple model, but you can use them directly in your applications, too. These tools are
part of the XML Developer’s Kit (XDK), which includes a robust set of classes that
allow you to interface with XML in your code. Both the Document Object Model
(DOM) and Simple API for XML (SAX) APIs are fully supported. In addition, you get
a lot of additional methods to make your life easier. These APIs are covered in depth in
Chapter 17.
This takes care of handling XML programmatically, but what about storing docu-
ments? New to Oracle 9i, you can store XML documents directly in the database using
the XmlType. This is an object type and takes advantage of the object-relational capa-
bilities of Oracle. Once in the database, you can use XPath, the XML search language,
to search XML and extract data from inside the documents. The searching of XML is
integrated with Oracle Text, so you can do complex text searches on an entire XML doc-
ument or just certain nodes inside the XML document. All of these capabilities are inte-
grated with Oracle SQL and are accessible from XSQL pages. You’ll learn more about
this capability in Chapter 11.
Oracle is already the best relational database on the market. Now, it is in step with the
latest advances in XML. Not only can you store and search XML inside the database,
you can also handle XML programmatically using Oracle-supported APIs. Throughout
the book, you’ll see how well Oracle is integrated with XML in all of its forms.
Introducing Oracle XSQL 13
Oracle JDeveloper
You can use any development tool you wish to develop XSQL pages, but Oracle JDe-
veloper offers some advantages. It is a development environment for building all types
of Java and Java 2 Enterprise Edition (J2EE) applications with great support for XML.
It highlights your text for you, provides easy lookup of Java classes, and checks the
syntax of your XML and XSLT documents. In addition, you get the project manage-
ment capabilities found in most development environments.
Because XSQL pages are also XML documents, the XML syntax checking will keep
your documents well formed. JDeveloper also provides specific support for XSQL
page development. It will perform XSQL-specific syntax checking and will interac-
tively execute your code. When you get into the development of action handlers and
serializers later in the book, you can use it to interactively debug your Java code.
The approach of this book is to be a development tool agnostic. All of the various
types of files—Java, XSLT, and XSQL—are all text files that can be developed with any
text editor. This said, JDeveloper is tuned for XSQL page development. If you haven’t
already found a development environment that you like, JDeveloper is an obvious and
promising candidate.
Introduction to XML
Now that you have an overview of the XSQL architecture, it is time to explore the nitty-
gritty of XSQL. As with any language, you also need to know a bit about the syntax.
Because all XSQL documents are also XML documents, the syntax is actually defined
by XML. XSLT is also an XML-based language, so the same is true for it. This means
that you need to learn a bit of the basics of XML.
XML is a very simple language. Its power lies partly in this simplicity. With XML,
you have an easy, industry-standard way to package data. Because XML parsers
are prevalent, anyone can parse your XML document and get at your data. Most
important, you can design exactly how you want your data structured. Here, you are
going to learn the basics of XML. Our aim is to educate you on the structure of XML
documents.
things, SGML was used in the documentation of the stealth bomber. A typical SGML
project results in the creation of a complex HTML-like language. In fact, HTML is an
application of SGML.
Why didn’t the Web creators just use SGML? SGML is much too heavyweight for
popular use over the Internet. Though the Web would be a richer place if everyone
used SGML to create their Web sites, no browser vendors were willing to implement
SGML. SGML would also dramatically increase the network burden. Perhaps most
important, SGML is complex to learn, whereas HTML is very simple. Without a simple
markup language like HTML, the Web probably would have never reached critical
mass.
So then, what’s wrong with HTML? While SGML is too complex, HTML is a bit too
simple for the demands of contemporary Web applications. HTML is intrinsically tied
to how documents should look to their users. Though HTML documents are highly
structured, they are structured around presentation. Consider the H1 tag. The text con-
tained inside of an H1 tag is usually a headline of some sort, but you don’t really know
much more about it than that. About all you know is that the Web page author wants
that text to stand out.
Along the same lines, consider Web sites that tell you when the Web site was last
modified. You can look at a table of contents Web page and it will tell you instantly
when the content was last changed. However, there is no way to look at the HTML
code and algorithmically determine when it was last updated. Even though HTML
documents are highly structured, they aren’t semantically structured. There is no way
to look at HTML and interpret it as much more than text data.
What would work is the ability to define your own tags somehow; however, HTML
doesn’t let you do that. The tag set is created by a standards body. Because all of the
browser vendors are expected to implement the latest features of HTML, any additional
functionality needs to be universally applicable. This is understandable, of course.
HTML represents the presentation layer and will never be applicable to the structure of
your data. What is needed, then, is a way to structure your data separate from HTML.
This is the value of XML. Instead of confining yourself to HTML, you are able to cre-
ate your own language for your application. Oracle’s canonical schema that you’ll
learn about in a few pages is an example of this. Oracle developed a way to represent
SQL result sets in XML. Once established, XSQL developers can use it to present and
manipulate the data. You also have the power to define your own markup language
and can do it very quickly. You don’t have to write a parser—XML parsers are widely
available. Your burden is simply to think about what you need, create an XML schema,
and document it. Once created, other people inside and outside of your organization
can use it for data interchange.
N OT E Defining your own tags is great, but an XML document isn’t instantly
useful by itself. While an HTML document is immediately usable by millions of
Web browsers worldwide, an XML document isn’t. You can create a
<LastUpdated> tag, but what application will understand it? Thus, XML is
primarily used, in conjunction with XSLT, to create HTML and other
standardized document types, or by a Web services client that knows the XML
schema you are using.
Introducing Oracle XSQL 15
As an XSQL developer, you will be creating your own implicit schemas along the
way. However, it is a bit grandiose to think of these as brand new markup languages.
In most cases, they are going to be the Oracle canonical representation plus some XML
that you add around the edges. Still, this does give you an XML layer to your applica-
tion that is separate from the actual presentation. In terms of XSQL development, this
is the key advantage of XML. You are able to separate your application data from the
actual presentation. XSLT yields the actual presentation while you are able to easily
repurpose the XML layer.
Document Structure
An XML document is structured like a tree. Trees are some of the most pervasive struc-
tures in all of computer science. Probably the most common usage is a file system.
Trees are a great elegant structure because they can be used to establish relationships
between any set of data. Even a table of data can be easily represented with a tree, as
you’ll see later in this chapter.
Trees can be generally described with the following rules:
■■ There can be only one root element.
■■ The root element is the only element that has no parent.
■■ Every nonroot element has exactly one parent element.
■■ Any element can have one or more children elements, or no children at all.
In XML, the parent-child relationships are represented by start and end tags. The
following example shows a simple document that contains two child elements.
16 Chapter 1
<?xml version=”1.0”?>
<Root>
<ChildElement1 />
<ChildElement2 />
</Root>
This should look very familiar to anyone who has coded HTML. The <Root> ele-
ment looks like a <UL> tag, and child elements look like <LI> tags. There is a key dif-
ference, though. If you look at the child elements, you’ll notice that the tag ends with
/>. This is required in XML so that the parser knows that this element has no children.
The preceding example represents the three tag types in XML: (1) the start tag, (2)
the end tag, and (3) the empty element tag. Table 1.1 documents the syntax of these
tags. In the “Form” column, the unique syntax requirements for each type of tag are
documented. There are also common syntactical requirements that apply to all tags.
As you would expect, start and end tags must be properly nested. A child element
must be entirely contained within its parent’s start and end tags. The following exam-
ple demonstrates bad and illegal nesting:
Our last structural consideration involves our children elements. In our previous
example, there were only two children elements, and each had a different name. This
isn’t a requirement. You can have many children with the same name. For instance, the
XML returned from the database by Oracle will have as many row child elements as
there are rows in the result set. Our final example is of a valid document with multiple
elements of the same name. Just for fun, this example is also more deeply nested.
Processing Instructions
You may have noticed that all of our sample documents begin with the same line. This
is a processing instruction. Processing instructions give input to the application that is
handling the document. The <?xml version=”1.0” ?> processing instruction is
required. It can also have an additional attribute to describe the character encoding of
the document. Here is an example that reinforces the default encoding:
Your documents can have more than one processing instruction. For XSQL pages,
your documents will usually have a processing instruction that references an XSLT
stylesheet. This will be covered in depth later this chapter.
Attributes
Attributes are name-value pairs that are associated with an XML element. Most HTML
elements also have attributes. The bgcolor attribute of the body element is one of many
examples. You can have as many attributes in your element as you wish. However,
each attribute name can appear only once for a particular element. Values always have
to be quoted with either single or double quotes. Attribute names and values are
restricted in that they can only contain some characters. These restrictions are the same
as the restrictions on the names of XML elements and are covered in the next section.
18 Chapter 1
Here is an example of an XSQL page where three attributes are used: <connec-
tion>, <xmlns:xsql>, and <tag-case>.
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query tag-case=”lower”>
select * from emp
</xsql:query>
</page>
Syntax Nitty-Gritty
You know almost everything that you need to know to create well-formed XML docu-
ments. There are a couple of rules that you haven’t seen yet, though. This section acts
as a review of the rest of the syntax rules. The most common restrictions that you will
encounter involve the names of elements and attributes. This is covered first. XML, like
most languages, has reserved characters. Sometimes, you’ll want to use these, so you’ll
learn how. One option you’ll learn about is the CDATA entity, which allows you to
define special sections of character data. The final section here covers XML comments.
There is one instruction that isn’t discussed here: <!DOCTYPE>. It is used to specify
a Document Type Definition (DTD), a type of schema. Before covering the rest of the
syntax rules of XML, let’s review the syntax rules that have already been covered:
■■ You must have an XML processing instruction at the top of the document.
■■ There can be only one root element.
■■ Start tags must have matching end tags, and vice versa.
■■ Tags must be nested correctly.
■■ A particular attribute can appear only once per element.
■■ Attribute values must be enclosed in single or double quotes.
Special Characters
There are several characters that have special meaning in XML. From time to time, this
will pose an inconvenience because you want to use these characters. The full set of
special characters is described in Table 1.3.
The most common problem encountered with XSQL is with the < symbol. This sym-
bol is also an operator in SQL, so its special status in XML causes problems. The fol-
lowing XSQL page will produce an error:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
select * from emp where sal < 50000
</xsql:query>
</page>
When the XML parser encounters the <, it thinks that it has encountered a new tag.
When it encounters the next character, a space, it gives up. There are two workarounds:
(1) Use the escape sequence as demonstrated in the following code, or (2) use CDATA,
which is covered in the next section.
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
select * from emp where sal < 50000
</xsql:query>
</page>
CDATA
The CDATA entity allows you to declare a section of character data off-limits to the
XML parser. When the parser encounters the CDATA declaration, it skips all characters
inside of it. Here is an example that resolves the earlier problem with the < operator:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
<![CDATA[
select * from emp where sal < 50000
]]>
</xsql:query>
</page>
CDATA entities are useful any time that you have sections that shouldn’t be
processed because they also take a load of the processor.
Comments
No programming language is complete without comments. The syntax for comments
in XML is as follows. It is identical to HTML comments.
Namespaces
As discussed earlier, XML allows you to create your own languages. However, what if
someone else has developed a schema that you want to use? There is the chance that
element names from this other schema will conflict with yours. XML has a solution for
this: namespaces. Namespaces allow you to make your elements globally unique. It
does this by attaching your element names to a Universal Resource Identifier (URI). A
Uniform Resource Locator (URL) is an example of a URI, as is a Uniform Resource
Name (URN). Even if you use an extremely common element name in your document,
such as name, you can make it globally unique by specifying a URI that you control.
XSQL uses a URN—<oracle-xsql>—for its namespace. You may have noticed it
in the examples:
The <xmlns:xsql> attribute signifies that some children elements of the page ele-
ment will belong to the XSQL namespace. Namespaces can overlap—in fact, that’s the
whole point. The following XSQL example shows this. This page uses both the XSQL
namespace and the Mike namespace.
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”
xmlns:mike=”http://www.ibiblio.org/mdthomas”>
<xsql:query>
select * from emp where ename=’SMITH’
</xsql:query>
<mike:mikeNode>
Value
</mike:mikeNode>
</page>
The output of this XSQL is shown above. Notice that the <xmlns:mike> attribute
is preserved but the <xmlns:xsql> namespace isn’t. In the output, no member of the
XSQL namespace is present. The <xsql:query> element was replaced with the
results of the query. Because there are no members of the namespace in the document,
the XSQL page processor removes the attribute.
Schemas
We spoke earlier about how XML allows you to create your own languages. These
languages are formally known as schemas. A schema is a definition of how elements in
your XML should relate to one another, what attributes are appropriate, and what
values are appropriate for nonempty elements. You can program very successfully in
XSQL without ever creating your own schema. At the least, it’s important to be con-
versant about schemas.
The XML documents that you have seen so far are very simple, but XML can be very
complex. The purpose of a schema is to rein in that complexity. This makes it easier for
applications to be able to consume the XML—they know what they are expecting.
22 Chapter 1
Since we’ve talked about creating your own languages with XML, perhaps we can
extend that analogy. If the elements are the words of our language, the schema is
the grammar. It tells the world how the words have to be arranged so that they are
meaningful to our applications. You read earlier about valid XML documents. A
schema is used to determine that an XML document is valid for that schema’s particu-
lar set of rules.
As with natural languages, there is a lot that can go into determining that a docu-
ment is valid. Think about it in terms of plain old English documents, such as this
book. On one level, this book is valid if the individual sentences are grammatically cor-
rect. The editors have certain requirements about section headings before they will call
it valid. The publisher wants the book to be a certain length and of a correct tone and
quality before it is considered valid to ship it to the stores. Ultimately, the reader makes
the call as to how valid the book is as a resource based on a number of factors.
The validity tests for XML documents can be as multifaceted as this. At the lowest
level, a schema can be used to determine if particular nodes have values of the right
type. The next step is to determine that the structure is right. Do the children elements
belong with their parent? Does the parent have all of the children nodes for it to be
valid? Then, you can look at how the different elements relate to each other to make
determinations of integrity. From there, the sky is the limit. If you desire, you can pile
complex business rules into your schema.
With an idea as to what schemas are about, it’s time to focus on how to implement
one. This can be as confusing as the complicated schemas we are talking about! There
are several different ways to define schemas. These are called schema languages.
(Good thing the languages we develop with XML are called schemas, or else they
would have to be called language languages!) The original schema language is DTD. In
fact, it has its own instruction built in to XML: <!DOCTYPE>. Though still widely used,
DTDs are becoming unpopular for a number of reasons, including cumbersome syntax
and the inability to define data types. The other popular schema languages are XML
based. W3C XML Schema is the heir apparent. There are other players, including
RELAX NG, Schematron, and Exampletron.
Moving On
In the previous pages, XSQL was covered at a high level. You learned the problems that
XSQL addresses and how it integrates the technologies together. The XML basics covered
in the previous section will come up again and again throughout the book. Now, it’s time
to dive into XSQL development. The next couple of chapters cover the installation and
setup of XSQL. After you have XSQL installed, you’ll be ready to move forward.
CHAPTER
Now that you have had a glimpse of all the wonderful things that you can do with
XSQL, your next step is to get it working and start developing. This chapter focuses on
getting it installed on your system, getting the demos up and running, and creating
your very first XSQL page. As with any new addition to your system, XSQL introduces
a new set of security concerns. Security is best attacked at the time of installation, so
you will learn about the security implications of XSQL in this chapter.
The good news is that XSQL is easy to install, and the security issues raised—though
important—are easy to understand and simple to resolve. If you are starting from
scratch, you should be able to get up and running in only a few hours. Most of that
time will be waiting to click the next button in the Oracle 9i database installation.
23
24 Chapter 2
If you are on your way to developing production applications with Oracle XSQL, at
some point you will need to install the Oracle XDK in a production environment. This
makes things a little more complex. For example, maybe you won’t be able to use the
default Web server that the Oracle Installer will install for you. If you are going against
a production database, you will probably have to change the configuration to point at
that database.
Although a little more complex, installing XSQL in an existing environment shouldn’t
cause any loss of hair or even loss of sleep. In the next few pages, you will start by cov-
ering the simple case of installing everything from scratch and then pay special attention
to issues that arise when installing in an existing environment. The last few pages are
spent getting you well versed in the security issues around XSQL so that you can feel safe
as you learn about this tool.
Basic Installation
It’s time to dive into the installation. Before starting, you will first look at the anatomy
of the components, so that you know what you are installing and how the pieces fit
together. The next step is to get the files, either from Oracle’s Web site or from your
Oracle 9i CD. From there, you will see how to install the whole system, including the
database and Web server, from scratch. Then you will look at how to integrate with
existing environments.
T I P If you haven’t already done so, you should sign up for the Oracle
Technology Network Web site (otn.oracle.com). Sign-up is free, and you get
access to a lot of resources, including downloads of the latest and greatest
Oracle technologies.
Installation Anatomy
Before blindly invoking the installer and pushing buttons, your first step should be
conceptual. What are all the pieces that you are installing? Figure 2.1 shows how the
pieces fit together from an installation perspective. This isn’t an all-inclusive diagram
of how a working XSQL system should work. You have a lot more fun stuff to add. For
now, this glimpse should be enough so that, in addition to successfully installing
XSQL, you will understand what you installed.
Here’s some more information about each of the components described in the
diagram:
■■ Oracle XSQL Servlet. This is the key piece of the puzzle. Most of this book will
focus on the functionality of this servlet.
■■ HTTP server. For the purposes of this discussion, the Web server handles HTTP
requests from the client.
■■ HTTP client. For now, this is a Web browser. Later on, you will see how this can
also be a Web Services client—instead of a person reading rendered HTML, a
piece of software will consume XML data.
Getting Started with XSQL 25
■■ Servlet container. A servlet container is a Java Virtual Machine (JVM) that will
invoke a servlet in response to a certain HTTP request. A servlet container is
usually one part of an application server. Also, J2EE containers superset the
functionality of servlet containers—a J2EE-enabled application server like IBM
WebSphere, BEA WebLogic, or IPlanet Application Server can be considered a
servlet container, just as Tomcat is a servlet container.
■■ XSQL files. These files describe the database queries and are the key pieces that
you, as a developer, add to an XSQL system. There are several examples files
included in the installation, and you will create your own from scratch before
this chapter is finished.
■■ JDBC driver. The XSQL servlet is written in Java, and the JDBC driver is neces-
sary to access the database.
■■ Oracle database. For your purposes, consider this an Oracle 9i database. How-
ever, Oracle XSQL isn’t limited to only 9i. Older Oracle databases can be used,
including Oracle 7.3. Database compatibility is determined by JDBC compati-
bility. Thus, non-Oracle JDBC-compliant databases can also be integrated with
XSQL. This book will focus on XSQL integration with Oracle 9i and how to
take advantage of the rich technology available only from Oracle.
You may be looking at the diagram and saying, “Wasn’t this supposed to be easy?”
Except for the Web browser, all of these components are installed by default. In fact,
you don’t need to know all of these details to successfully complete a scratch installa-
tion. However, this anatomy lesson will become important if you ever have to install
XSQL in an existing environment.
Internet
HTML
Files
HTTP Server
Servlet Engine
XSQL Servlet
JDBC
XSQL, XSL
Files
Database
Figure 2.1 Anatomy of an installation.
26 Chapter 2
Unix Prerequisites
Before beginning a Unix installation, you need to create a dba group and an oracle
id. The dba group allows several users on the system to control the database. The
oracle user actually owns the database. Depending on your particular system, you
may need to tweak the kernel parameters so that the System Global Memory (SGA)
structure of the Unix system can be accommodated. In most cases, this step isn’t
necessary for development systems of adequate hardware that won’t be bearing a
heavy load. However, you may wish to consult your OS readme file on this subject.
After this, your first step is to create the dba and oinstall groups. This can be
completed by the groupadd command as root. On Red Hat Linux, this can be as fol-
lows. The -f flag causes the command to exit with an error if the group already exists:
# groupadd -f dba
# groupadd -f oinstall
With this step completed, you should add any users to these groups that should be
able to perform database administration or installation tasks, respectively. The next
step is to create the oracle user with the useradd utility. After creation, you have
several configuration tasks for the oracle user:
■■ Set the umask to 022. Depending on the shell, this should be done in the
startup file—either the .profile or the .cshrc file.
■■ Set the ORACLE_BASE environment variable in the startup file to where you
want Oracle products to be installed.
■■ Set the ORACLE_HOME environment variable to where you want the database
installed. This should probably be a subdirectory of ORACLE_BASE.
Getting Started with XSQL 27
■■ Before preceding, either exit the session and reenter it, or refresh the session
with the new settings (e.g., source .cshrc).
After these steps are complete, you are ready to begin the Oracle installation.
Simply cd in to the CD mount point (e.g., /cdrom) or the top level of the untarred
distribution and execute runInstaller. If you are doing the installation remotely,
you will not be able to complete the installation from the command line. You’ll need
X Windows. Make sure that you have X-Server running on your local machine and
that the remote machine isn’t restricted from your X-Server. Then, set the
DISPLAY environment variable to your console. If your local machine name is my
.localMachine.com and the machine where you are installing Oracle is running
CSH, the following will work:
If you are going to be working remotely with this machine a lot, you might want to
go ahead and add that to the startup script.
Your first step is to click Next. There are really only a couple of choices that need to
be made in the next few screens. The default choices should be adequate for creating a
development system. The most important choice comes on the File Locations screen
(Figure 2.3). Before choosing the location for the Oracle installation, you should make
sure that you have enough space on the drive or file system. You should have at least 2
GB free. However, you don’t have to plan to have enough space on the given drive or
file system for all the data that could possibly be put into your database. As your data-
base grows, you can easily add new data files on other drives or file systems.
The other choice that you will need to make is the System Identifier (SID) of your
default Oracle instance. The preferred SID is ORCL, and using this name means that the
XSQL samples will work upon completion of the installation. If you name it something
else, you will need to tweak some settings in the XSQLConfig.xml file before the
samples will work. The other setting in this screen is the Global Database Name. This
should be the full Domain Name Service (DNS) hostname of the machine where you
are installing Oracle prefixed by the SID. This isn’t the only instance that can be
configured on this machine. You can configure as many instances as you would like. If
you are experimenting with different configurations, you can keep your experiments
largely separate by setting up different instances.
The Summary screen is the last screen that appears before you click Install to start
the installation (Figure 2.4). If you took all of the defaults, you should be able to scroll
down to the end of the list and see XSQL Servlet as shown. If you choose to do a custom
installation, you will need to scrutinize this screen carefully.
Now the installation is ready to begin in earnest. Depending on your system, it will
probably take at least a couple of hours to complete. Upon completion, you should be
able start the Web server and access the samples at http://localhost/xsql/.
Configuring Java
As you progress through the book, you will program in Java to enhance the core
functionality of XSQL. If you haven’t already installed Java, you should do so now.
Installation of the Java Developer’s Kit (JDK) is easy. Just go to www.javasoft.com,
download the JDK version that you want for your operating system, and install. Be
careful, however, which JDK version you download. It should be a supported version
listed in the release notes for your version of XSQL. The release notes are available at
http://localhost/xdk/java/xsql/readme.html after you have completed
the installation. This book is based on XSQL version 9.0.1.0.0 and JDK 1.3.
You will also need to modify the CLASSPATH before doing Java development. The
following Java Archives (JARs) need to be included in the CLASSPATH:
■■ jlib/sax2.jar
■■ rdbms/jlib/xsu12.jar
■■ lib/xmlparserv2.jar
■■ lib/oraclexsql.jar
■■ jdbc/lib/classes12.jar
30 Chapter 2
N OTE Later in the book, you are going to look at how to integrate Java Server
Pages (JSP) with XSQL. Most application servers that support servlets also support
JSP. JSP certainly isn’t a requirement for working with XSQL, but it is nice in many
ways. If you are at the point of making a decision as to which application server to
choose, you may want to consider one with strong JSP support.
The next section covers the Web servers that have been tested by Oracle with XSQL.
Following that is one example installation using Apache Tomcat.
this writing. If your particular servlet engine isn’t listed here, all hope is not lost. This
list represents only what Oracle pushed through its quality assurance labs. If you have
other servlets running, in all likelihood you will be able to get XSQL Servlet running in
your system.
These servlet engines were tested and verified with the XSQL 9.0.1.0.0 release:
■■ Oracle Internet Application Server 8i
■■ Allaire JRun 2.3.3 and 3.0.0
■■ Apache 1.3.9 with JServ 1.0 and 1.1
■■ Apache 1.3.9 with Tomcat 3.1 or 3.2 Servlet Engine
■■ Apache Tomcat 3.1 or 3.2 Web Server + Servlet Engine
■■ Caucho Resin 1.1
■■ Java Web Server 2.0
■■ Weblogic 5.1 Web Server
■■ NewAtlanta ServletExec 2.2 and 3.0 for IIS/PWS 4.0
■■ Oracle8i Lite Web-to-Go Server
■■ Oracle8i 8.1.7 Oracle Servlet Engine
■■ Sun JavaServer Web Development Kit (JSWDK) 1.0.1 Web Server
It’s important to note that this is a list of the servlet engines, not necessarily Web
servers. Your Web server may not be listed here, but a servlet engine that is compatible
with your Web server may be. For instance, in the case of Microsoft IIS, you can use
ServletExec and JRun because they can be installed on top of IIS.
■■ jlib/sax2.jar
■■ rdbms/jlib/xsu12.jar
■■ lib/xmlparserv2.jar
■■ lib/oraclexsql.jar
■■ jdbc/lib/classes12.jar
■■ xdk/admin
The last entry is necessary so that the XSQL servlet can find its configuration file,
XSQLConfig.xml.
The second step is to map the .xsql extension to the XSQL servlet. This tells the
servlet engine to hand requests for .xsql pages over to the XSQL servlet. This map-
ping is usually accomplished as a name/value entry in a configuration file or in an
administrative user interface. Here is an example of how to accomplish this for Apache
JServ. This entry goes in to the jserv.conf file.
The last step is to set up a virtual directory. This isn’t completely necessary, but there
are many advantages to having your XSQL pages in one area. Also, your demos won’t
work quite right unless they are inside an /xsql virtual directory. This is usually
handled at the Web server level, though Tomcat handles it as part of the configuration
of a context. For Tomcat, the context handles both the mapping mentioned previously
and the establishment of the virtual URL mapping. On Apache, the virtual directory is
configured as follows in the httpd.conf file:
Moving On
This chapter helped you through an XSQL installation. You should now have XSQL set
up and be ready to move forward. The next chapter focuses on getting the demos
configured and understanding security issues. From there, you’ll be ready to start real
development.
CHAPTER
Hello, XSQL!
At this point, you have a development environment for XSQL. You’ve been able to
confirm that the simplest demos work. By the end of this section, we are going to have
created our first XSQL page. First, you will complete the base installation by installing
the rest of the samples. Then, the development environment will be readied by
straightening out the Java CLASSPATH. Next, you will create a new Oracle user and a
connection definition in the XSQLConfig.xml file. Last of all, you will create your
very first XSQL page and a XSLT stylesheet to go along with it.
35
36 Chapter 3
If you are working with a database other than the default for your instance of
SQL*PLUS, you will need to include that in the connection string. If the database is
SAMPLE_DB, this would be as follows:
There is one last step to complete for loading the demo data. The doyouxml exam-
ple includes some XML data that needs to be imported into your database. Change into
the doyouxml/ directory, and load this with the import utility as follows:
With these steps completed, you should be able to view all of the demos. Figure 3.1
shows what the airport code display demo should look like after doing a lookup on
Washington.
>sqlplus SYSTEM/MANAGER@ORCL
Once you are logged in, you can create a user as follows:
Now that the user is created, log out and log back in as the user:
>sqlplus MOMNPUP/MOMNPUP@ORCL
The table that you will create is very simple. It will keep information about employ-
ees of the company.
With the following statements, you will create the table and populate two rows of
data.
T I P Our task here was easily completed at the command line. However, if you
are new to Oracle, you should definitely check out Oracle Enterprise Manager.
This is a Graphical User Interface (GUI) that allows you to perform tasks like
these with a few mouse clicks. Even better, Enterprise Manager will show you
the SQL that it uses to complete the tasks that you define graphically.
<connection name=”momnpup”>
<username>momnpup</username>
<password>momnpup</password>
<dburl>jdbc:oracle:thin:@localhost:1521:ORCL</dburl>
<driver>oracle.jdbc.driver.OracleDriver</driver>
</connection>
If you are going against a database other than ORCL, you will need to replace the
SID in the dburl.
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”helloworld.xsl”?>
<page connection=”momnpup” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
SELECT * FROM EMPLOYEE
</xsql:query>
</page>
<?xml version=”1.0”?>
<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”
version=”1.0”>
<xsl:import href=”../common/rowcol.xsl”/>
<xsl:template match=”page”>
<html>
<head><link rel=”stylesheet” type=”text/css”
href=”../common/rowcol.css” />
40 Chapter 3
</head>
<body class=”page”>
<center></center>
<xsl:apply-templates select=”ROWSET”/>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
You will learn more about XSLT stylesheets in Chapter 13. For now, you should
focus on getting the example working. Most important, if you didn’t create the exam-
ple in the xdk/demo/java/xsql directory, this example won’t work. The
xsl:import element references another stylesheet in the xdk/demo/java/
xsql/common directory. A lot of the complex work is done in that stylesheet.
In this file, you can see the basics of how XSLT works. First, notice that the XSL
stylesheet is almost entirely HTML. The idea is that the stylesheets describe how
the XML elements should be rendered in HTML. If you look back at the XML file,
you will see that it has a ROWSET element that contains ROW elements. Now, look at the
line <xsl:apply-templates select=”ROWSET”>. When the XSLT processor
encounters this element, it selects the ROWSET element in the XML file and renders it
according to the ROWSET template contained in the rowcol.xsl file.
The in-depth discussion is later. Your work isn’t finished until you tie the stylesheet
to the helloworld.xsql file. You can do this by adding the following as the second
line in the helloworld.xsql file:
XSQLConfig.xml
At this point, you have at least done a little work in the XSQLConfig.xml file. If your
environment was unorthodox, you may have already spent more time with this file
than you would like. Now you are going to learn more about this configuration file and
the role that it plays. First, you will learn it’s overall purpose and place in the XSQL
architecture. Then you will look at the individual settings available, one by one.
Because the config file is an XML document, it has a tree structure and resembles
a file system. There are top-level elements that contain second-level elements, the
second-level elements contain third-level elements, and so forth. XML is ideal for con-
figuration files. It allows related configuration parameters to be grouped together and
makes it easy to configure an indeterminate number of the same entities.
For this section, you cover the configuration elements from the top down using a
depth-first approach. This means that you will go all the way down one branch of the
tree then recurse back up the tree and go all the way down the next branch.
XSQLConfig Element
This is the top-level element that contains all other elements. It is required but has no
configuration options.
Servlet Element
This element has several children elements that configure the behavior of the XSQL
servlet. The servlet element itself has no configuration options. Table 3.1 shows the two
children elements that can be configured.
Processor Element
This section covers database caching, file caching, and security-related parameters.
The processor element has no configuration options. Table 3.2 details the immediate
children that have direct configuration parameters.
Beyond these elements, there are several that are more complex. The first is the
stylesheet-pool element. This element defines the characteristics the individual pools,
while the stylesheet-cache-size parameter described in Table 3.2 specifies the number
of individual pools. Table 3.3 details the options of the children of the stylesheet-pool
element. The stylesheet-pool element itself takes no parameters.
Here is an example of the stylesheet-pool element:
<stylesheet-pool>
<initial>1</initial>
<increment>1</increment>
<timeout-seconds>60</timeout-seconds>
</stylesheet-pool>
<connection-manager>
<factory>oracle.xml.xsql.XSQLConnectionManagerFactoryImpl</factory>
</connection-manager>
The next element to explore is the security element. The configuration elements here
will factor heavily into our security discussion at the end of the chapter. Here we detail the
various elements and the effect they have. The security element has one child element,
stylesheet. Neither of these elements have any configuration options. The interesting
elements are children of stylesheet. All of the following examples should be included
between <security><stylesheet> and </stylesheet></security>.
<defaults>
<allow-client-style>no</allow-client-style>
</defaults>
<trusted-hosts>
<host>127.0.0.1</host>
<host>trustedHost.trustedDomain.com</host>
</trusted-hosts>
Http Element
The http element is a very simple element. It allows for the definition of an HTTP
proxy server for use by the XSQL servlet. If you are going to grab documents from
beyond your firewall and need to use a proxy server to access them, you define it here.
Here is an example:
<http>
<proxyhost>your-proxy-server.yourcompany.com</proxyhost>
<proxyport>80</proxyport>
</http>
Connectiondefs Element
The connectiondefs element describes the connections to the database. Each XSQL
page specifies the connection that should be used to process the SQL statements contained
in that page. The connectiondefs element and the connection element itself has no
configuration options. Table 3.5 describes the child elements of the connection element.
Hello, XSQL! 45
Generally, you set up a new connection because you need to access the database as
a different user or you need to access a different database. To access as a different user,
simply copy and paste a working connection element and change the user name and
password. Configuring access to a different database is more complex.
Actiondefs Element
The actiondefs element configures action handlers that can be called from an XSQL
page. You will learn more about action handlers in Chapter 8. Here is an example
showing how to configure a new action handler:
<action>
<elementname>current-date</elementname>
<handlerclass>
oracle.xml.xsql.actions.ExampleCurrentDBDateHandler
</handlerclass>
</action>
Serializerdefs Element
The serializerdefs element configures serializers that can be called from an XSQL
page. You will learn more about serializers in Chapter 19. Here is an example showing
how to configure a new serializer:
<serializer>
<name>FOP</name>
<class>
oracle.xml.xsql.serializers.XSQLFOPSerializer
</class>
</serializer>
46 Chapter 3
Security Issues
Now that you have a new package installed and understand how it works, it is a good
time to consider the security implications. XSQL doesn’t introduce grave and complex
security concerns. However, it is a new window onto your system from a public
network. This gives hackers a new avenue of attack not only on your system but also
your entire enterprise. Because XSQL attaches directly to the database, the potential of
information compromise is especially high.
We can’t promise that reading the next few pages will prevent hackers from pene-
trating your system. The very nature of Internet security dictates that you have to insu-
late yourself from vulnerabilities that aren’t currently known. What you will learn here
is what the current vulnerabilities are and what some past weakness have been. By
using these known issues as a case study, we will explore various strategies to protect
yourself from XSQL-based attacks.
Known Issues
There are a couple of current and known issues involving XSQL. These are issues, not
security holes. A security hole is a flaw in the design and implementation of a product
that isn’t documented. It gives hackers a secret route to exploit your system. The issues
that you are going to learn about here are: the need to protect the XSQLConfig.xml
file, the problem of SQL poisoning, and the implications of denial-of-service (DOS)
attacks. You will also see a true security hole that was found in 2001, which has subse-
quently been resolved. The ability to pull in stylesheets from other hosts meant that it
was possible to execute arbitrary Java code on the Web server machine. Though not a
problem now, it is valuable to know how XSQL was compromised in the past so that
you can think about the overall architecture in terms of security.
SQL Poisoning
One thing that makes XSQL great is that it is very easy to create Web pages that are based
on SQL queries. This also is a security issue. As you will learn in detail in Chapter 6, it is
easy to plug parameters into your queries. Without jumping ahead too much, let’s look
at an example of this functionality and then discuss the security ramifications of it:
<?xml version=”1.0”?>
<xsql:query connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
SELECT {@field} FROM EMP WHERE ENAME = ‘{@name}’
</xsql:query>
You can use this XSQL page to provide the data of a particular employee by name.
The name parameter can be specified in the URL embedded in the query string. If the
preceding example is saved as hack.xsql into the momnpup directory created earlier,
you should get the output that is shown in Figure 3.4 when you access http:
//localhost/xsql/momnpup/hack.xsql?name=ADAMS&field=job.
If your users are clever, they can even do their own queries. The URL http:
//localhost/xsql/momnpup/hack.xsql?field=ename,job&name=ADAMS’%2
0OR%20ENAME=’ALLEN can select a couple of employees at once, as shown in Figure 3.5.
However, it is easy to abuse this. In the next example, your user goes from benignly
saving himself or herself some time to getting information that he or she shouldn’t see.
In this example, the hacker uses knowledge of the EMP table combined with a clever
“where” clause to get the salaries of all of the workers. The URL used is http:
//localhost/xsql/momnpup/hack.xsql?field=ename,sal&name=ADAMS’%
20OR%20NOT%20ENAME=’ZZZZ. The result is shown in Figure 3.6.
It’s important to note that the programmer that created this code walked right into
this kind of attack. By parameterizing too much of the SQL statement, hackers are able
to access secret information through a Web browser by coming up with their own SQL
statements. Even though this example may look a bit contrived, it’s important to note
that this is only one example of this type of hack. XSQL yields a great deal of power in
this regard, but as you design and implement XSQL pages, you should think about the
security implications. In Chapter 6, you will see how to use the flexibility safely.
Denial-of-Service Attacks
The first thing that should be said about denial-of-service (DOS) attacks is that it is
hard to prevent them at the application level. The strategy behind DOS attacks is to so
overwhelm your servers that it puts you out of business. They don’t exploit a particu-
lar hole in your application that you can plug. Instead, they attempt to overwhelm
your system through a mass of legitimate requests. The best you can do at an applica-
tion level is attempt to sense that you are being attacked and then try to block the
individual attackers, but this is risky. If you try to do this based on IP, for instance, you
might find yourself cutting off all AOL users from your system. AOL tends to proxy
lots of users through the same IP.
Instead of attempting to block DOS attacks, let’s look at how to sustain them and min-
imize their impact on your operations. You should assume that your firewall engineers
will ultimately defeat a DOS attack, after some good sleuthing as to their origins and
some cooperative action with the ISPs from where the attacks originated. As an XSQL
application developer, your responsibility is to survive the (hopefully temporary) siege.
During a DOS attack, your Web performance to legitimate users will suffer. Stopping
that suffering is the responsibility of your security engineers. Because XSQL applications
are database driven, your responsibility is to protect the database. DOS attacks have two
avenues of attack on your database: (1) resource overload and (2) data overload.
A DOS attack pointing at your database will slow the database down for legitimate
users. It could even cripple it. This is unfortunate for your legitimate Web-based users.
After all, during the initial siege you can’t tell the difference between your legitimate
Web-based users and the Web-based hackers. However, it is likely that your database
has more than one face. It may have multiple distinct Web faces, and internal users
may use non-Web tools to access it.
Therefore, the contingency strategy is to sustain an attack on one front while still
operating relatively well on the other fronts. Oracle’s distributed database technology
is very good at providing this if implemented correctly. While the attack pegs the
central processing units (CPUs) on the machines that service the Web, other machines
that comprise the same distributed database can serve their users more or less nor-
mally. This also makes it easier on everyone if you have to shut down the besieged Web
interface for a while.
Along the same lines, you can take a data warehousing approach. In some cases, this
can be a little simpler than implementing a full-blown distributed database. If your
Web interface mainly provides querying and the data doesn’t need to be synchronized
50 Chapter 3
in real time with the rest of your operations, you may want to offload the necessary
data from your internal operational systems to your dedicated Web interface database.
Then if this machine heats up, it doesn’t risk interfering with the rest of your operation.
The other problem is data overload. As you will see, your XSQL application can
enter and modify information into your database, such as orders and registrations.
First, you should consider how to ensure that such information is legitimate and how
you would either block illegitimate information or at least be able to back illegitimate
information out of the database. However, this is only one side of the problem. The
other side is that you can easily fill up all of your drives with bogus data. There are two
approaches to this: Either (1) buy more drives or (2) put quotas on how much space
your Web interface can generate. After the quota is reached, no more data for that user
can be entered. However, that may be preferable to preventing other database applica-
tions from working. More information about setting quotas for users can be found in
your Oracle Database Administrator’s Guide.
Keep Up-to-Date
A lot of attacks, such as the popular e-mail worms, are exploits of known vulnerabilities.
You can save yourself a lot of pain and embarrassment simply by keeping up-to-date.
Oracle is highly committed to keeping its customers secure, so you can count on them
to be proactive in addressing concerns. Still, someone in your organization will have to
watch for the emails and apply the patches. You can also keep an eye on security
through some focused Web sites, such as www.secureroot.com.
Moving On
In this chapter, you went from a basic installation of the files to actually seeing XSQL
in action. You know how to secure your XSQL applications and how to configure all of
the options in the XSQLConfig.xml file. Now you’re ready to go! The next four
chapters cover the workings of XSQL and the details of all of the XSQL features. With
this base knowledge, you’ll branch out to the other key technologies, starting with
Oracle SQL. You’ll then learn all about XSLT. The book wraps up by showing you how
to extend XSQL with Java. Now that you have a development environment set up, it’s
time to put XSQL to work!
CHAPTER
XSQL Architecture
You have already seen a lot of XSQL architecture through the install. In this chapter,
you will refine your understanding and explore new parts that you didn’t see in the
earlier chapter. You’ll start with an overview and then dive into the specifics of data-
base access and presentation. From there, you’ll explore the concept of actions. For this,
you have a head start—you used actions when accessing the database in the last chap-
ter. Then, you’ll look at action handlers, which allow you to create your own types of
actions. The final concept you’ll examine here is serializers.
Overview
In looking at the high-level architecture, the first step is to separate the XSQL servlet
from the XSQL page processor. The XSQL servlet calls the XSQL page processor to exe-
cute XSQL pages. The XSQL page processor interfaces with the database through JDBC
and uses XML SQL to create the XML datagram. The XML processor loads the XSQL
page that is specified by the URI used to invoke the XSQL servlet. The XSLT processor
loads the XSQL page if it specifies an XSLT stylesheet. After processing, the result is
returned to the client via the XSQL servlet and Web server. The high-level view is
diagrammed in Figure 4.1.
53
54 Chapter 4
Web Server
Servlet Engine
XSQL Servlet JSP Runtime
jsp:forward
or
jsp.include
The JSP runtime is also shown in the diagram. As with any servlet running in the
context, you can integrate JSP and XSQL via the jsp:forward and jsp:include
tags. The jsp:forward tag tells the XSQL servlet to handle the request, while
jsp:include can be used to include the XSQL result in the page that JSP produces. If
you are using some other kind of scripting language, such as Cold Fusion, or if you are
using an active server page (ASP), you should also be able to redirect to an XSQL result
by simply redirecting to the XSQL servlet.
For 90 percent (and perhaps all) of your XSQL pages, you’ll use the XSQL servlet.
However, this isn’t the only way to invoke an XSQL page. You can also invoke an XSQL
page either by using the XSQL command line utility or programmatically. In such
cases, the XSQL servlet isn’t involved at all. These components interface directly with
the XSQL page processor. They are discussed in more depth later in the chapter.
Now it’s time to figure out how all of the pieces fit together. Hopefully you got some
hands-on experience with this in the previous chapter. But in the heat of getting every-
thing working, it’s sometimes hard to think conceptually. So now, each piece will be
examined.
Java Parts
XSQL is written in Java and uses the server-side Java architecture. It isn’t strictly
necessary to be a Java pro to do great things with XSQL. But understanding the basics
XSQL Architecture 55
of Java architecture can help you in understanding the big picture. This section covers
the Java Virtual Machine (JVM), JDBC drivers, servlet engine, and JSP.
code is more efficient than common C++ code. Other areas to consider, beyond speed,
are the ease of programming and the maintainability of the code. Java has many
advantages over C++ in these areas. In terms of architecture, the most important one is
the garbage collector.
The garbage collector is used to manage memory on behalf of your code. In C++,
you have to manage your own memory. You have to ask for memory, and when you’re
done using it, you have to explicitly return it to the operating system. It’s very easy for
programmers to mismanage memory. When they do, their programs often either crash
outright or experience a memory leak. A program-leaking memory will eventually
consume all of the system’s memory and crash.
The Java garbage collector makes the your life as a programmer easier. You aren’t
able to explicitly ask for memory; instead, memory is allocated when you need it, upon
the creation of objects. Most important, you don’t have to explicitly declare when you
no longer need the memory you are using. The garbage collector keeps track of the
objects that you are using by keeping track of the references to that object. When there
are no more references to an object, the garbage collector will mark it for garbage
collection. The next time that garbage collection is performed, the memory will return
to the operating system.
Driver
Java Database Connectivity (JDBC) is a key piece in server-side Java development. It
yields a standard mechanism for accessing SQL databases from Java applications. The
XSQL page processor uses JDBC to get to your database. Since the XSQL page proces-
sor interfaces with JDBC, you can actually use any database with XSQL for which there
is a JDBC driver.
The best driver to use with XSQL is the Oracle Type 4 JDBC driver that installs with
Oracle 9i. This driver does a direct connection to the database without any intermedi-
ary client interface, and it is superior to the more primitive JDBC drivers that sit on top
of Object Database Connectivity (ODBC) and other native libraries. These features lead
to less portable architecture and that you inherit any problems of the lower interface.
Servlet Engine
The servlet engine either uses or is run inside a JVM. What defines a servlet engine is
its ability to run servlets. Servlets are Java objects that, at their simplest, receive
requests from the Web and write output to the Web. Also, they can read and write to
the file system, transact with the database, and even make network connections to
interact with other services.
In strictly technical terms, a servlet is any Java class that implements the javax
.servlet.Servlet interface. Most servlets actually implement the javax.servlet
.http.HttpServlet interface, which is a subinterface of javax.servlet.Servlet
and is designed for HTTP. This interface is a set of methods (the Java name for subrou-
tines) that define how a servlet will react to a HTTP request. Generally speaking, a Web
server receives the HTTP request, determines that it should go in the servlet engine,
and hands it off. The servlet engine looks at its configuration information and figures
out which servlet should handle it. It then calls the appropriate methods of the
XSQL Architecture 57
JSP Runtime
The Java Server Page (JSP) runtime handles requests for JSP pages are similar to PHP,
ASP, and Cold Fusion pages. You can mix HTML (or another presentation-level lan-
guage) with Java code giving you the ability to easily create dynamic pages. It’s a much
better way than trying to create HTML directly with a servlet. A servlet-to-HTML
architecture has, usually, two hard and messy paths. In one path, you end up with a
bunch of HTML code inside your servlet code—not fun to write or maintain. In the
other path, you leave the HTML outside the servlet and read it in, doing the appropri-
ate gestations in your code to get the output you want. In this case, however, you have
to write a parser. This scenario is better than having your servlet junked up with
HTML, but using JSP or XSLT is easier.
JSP or XSLT, that is the question! Lots of smart programmers are debating this ques-
tion. You’ll get some perspective on this question later in this chapter. For now, you’ll
focus on JSP and how JSP integrates with XSQL.
The key requirement is that the JSP page and the XSQL servlet have to live in the
same context. The included call may look like a URL, but it isn’t quite. The XSQL
servlet isn’t invoked through the network; rather, the servlet engine calls XSQL servlet
directly as if it were called from the Web.
Why are JSP and XSLT sometimes viewed as competitive technologies? Both can
merge static HTML with dynamic data to create a dynamic page. How they do this is
radically different. XSQL is focused on the XSLT solution, and so is this book. But since
you’ll be getting so much XSLT in this book, you should know what JSP brings forth.
JSP gives you all the power of a procedural, object-oriented language. You can do all the
basic programming stuff, such as variables, for loops and conditional expressions. You
can even create and manipulate objects. In fact, you could easily write your whole appli-
cation in JSP! Doing so, however, will lead to some very messy, hard-to-maintain code—
one of the biggest complaints with JSP. The generally accepted design pattern for JSP is
to have the heavy lifting done by Java classes and then write very minimal scriptlets
that get string data from the underlying classes. In this way, you keep the JSP as clean as
possible—a very important condition. It is widely agreed that the look and feel of your
pages should be cleanly separated from the programming. If your Web designer can’t
easily make changes to the Web site without consulting a programmer, your site will
have lots of maintenance headaches and probably some unhappy staffers.
58 Chapter 4
XSLT is very well tuned for this separation. With XSLT, there is simply no way to
code your entire application in the stylesheets. XSLT simply isn’t that kind of language;
it’s a declarative language, like HTML. Since XSLT and JSP are essentially cousins, they
fit very well together. However, this relationship is also a weakness of sorts. There are
problems that JSP can easily solve that require some stretching with XSLT. Working too
hard in XSLT generally means that the problem should have been addressed and solved
elsewhere in the architecture. However, architecture is not taught in beginning com-
puter classes; procedural programming, however, is. Thus JSP is appealing because it is
more like the programming that people have been taught and are used to.
Ultimately, though, the problem is one of separating presentation from the pro-
gramming logic. XSLT guarantees this separation in a standardized way. JSP can
provide this separation, but the developers and designers working on the system have
to learn how this separation is made on a project-by-project basis. Unfortunately, this
architectural separation is often marred by late night programming in advance of
important deadlines.
There are factors that play heavily in favor of JSP. XSLT, after all, is mated to XML. If
you don’t come up with an XML document, the XSLT processor isn’t going to do a lot
with your stylesheet. In some cases, it doesn’t make since to translate your data into
XML just so you can use XSLT. If all you have is simple string data and you aren’t going
to do a lot of complex processing, using JSP may be the easiest solution. However, XML
is becoming more and more prevalent. This reasoning makes no sense for database-
driven applications because of the abundance of tools that translate the results of SQL
queries into XML automatically for you.
Faces Of XSQL
When most people think of XSQL, they think of a way to easily create dynamic Web
pages. This is what XSQL is best known for. But this functionality, which is provided
by the XSQL servlet, is only one way to access the power of XSQL. You can also use the
XSQL command line utility, as well as access XSQL programmatically. In the latter case,
you embed calls to XSQL inside your Java code. This section looks at each of the ways
to call XSQL. Each reaches the XSQL page processor, covered in the next section,
differently.
XSQL CommandLine
.xsl .xsql
Database
Figure 4.2 XSQL command line utility.
Why would you want to use XSQL from the command line? There are a few good
reasons. In a lot of situations, the data in your database may not be changing fre-
quently. In such a case, it may make more sense to generate an HTML page once a day
by using XSQL. When your users access the server, they won’t create any work for the
database. Instead, the Web server just picks up the HTML that has already been created
from the file system.
Also, XSQL and XSLT make a good pair for general file processing. In an example
given in Chapter 15, you’ll learn how to use the XSQL command line for routine tasks
like creating SQL scripts. Although you will probably use XSQL most frequently for
Web interaction, XSQL certainly isn’t limited to that. You can easily make use of the
XSQL command line for more general tasks.
XSQLRequest Class
XSQL request, diagrammed in Figure 4.3, is the programmatic interface to XSQL. With
it, you can invoke an XSQL page and process the results inside your programs. It
works much like the command line utility.
XSQL request has many uses. One is that it gives you a very easy way to invoke SQL
queries without burying them in your Java code. Another is that instead of getting a
result set back from the database, you get XML. Now, there are many times that you
would rather have the result set. However, if you are creating an XML-based applica-
tion, it would be desirable to already have the data in the correct format. You’ll learn
more about programmatic invocation and the XSQL request class in Chapter 17.
60 Chapter 4
YourJavaClass
XSQLRequest
.xsl .xsql
Database
Figure 4.3 XSQL programmatic invocation architecture.
XSQL Servlet
XSQL servlet invokes the XSQL page processor. You will have a lot of interaction with
the XSQL servlet. As described in the previous chapter, you must register the XSQL
servlet with the servlet engine. There are also two servlet-specific configuration ele-
ments: output-buffer-size and suppress-mime-types.
Though XSQL servlet is the Web interface to all XSQL functionality, it itself does lit-
tle. It receives a request and passes it to the XSQL page processor. The XSQL page
processor does the heavy lifting and hands back a result. If you have buffering turned
on, the XSQL servlet will buffer the output for you. If the XSQL servlet is configured
appropriately, it will suppress the sending of character-encoding information. And
that’s about it.
It’s time to move on to the real player—the XSQL page processor!
to the database, and gives you back something presentable. At the highest level, it per-
forms this function as described in Figure 4.4. This invoker is the XSQL servlet, the
XSQL command line, or the XSQL request. At the time of invocation, it is possible to
know how the XSQL page processor was invoked. For this discussion, you’ll learn how
the XSQL page processor behaves regardless of how it is invoked.
The XSQL page processor is already loaded into memory at the time of invocation
except when you call it from the command line or make the first call for the instance of
the JVM. When the XSQL page processor loads, it reads its XSQLConfig.xml file.
At about a 5,000-ft level, the XSQL page processor does the following when invoked:
1. Parses the request from the invoker
2. Selects the XSQL page
3. Processes the actions described in the XSQL page (most of the time by going to
the database)
4. Applies an XSLT stylesheet, if one is specified, to the resulting XML datagram
5. Passes the result back to the invoker
But alas, it couldn’t possibly be that simple! The engineers at Oracle have always
been known to put some brilliant, elegant machinery under the covers. You’ve already
had a glance at some of these widgets. In the previous chapter, you read about all the
configuration parameters in the XSQLConfig.xml file. In that discussion, you learned
about the XSQL page cache, the stylesheet cache, and the database connection pool.
Each of these are modules in the XSQL page processor. They ensure that the XSQL page
processor can drive your application with high efficiency. Figure 4.5 gives a detailed
view. You’ll learn about the two caches here; in the next section, you’ll learn about the
database connection pool.
Invoker
.xsl .xsql
Database
Figure 4.4 High-level diagram of the XSQL page processor.
62 Chapter 4
Invoker
XML SQL
XML XSLT
Parser Processor
.xsql
files .xsl
XSQL Page files
Connection Pool
Database
Figure 4.5 The XSQL page processor.
We begin with the XSQL page cache. This is a simple least recently used (LRU)
cache. When the cache is full and a page not in the cache gets called, the page in the
LRU cache is expelled. The algorithm, an LRU algorithm, is used to determine this
action. So, if one page gets hammered a lot, it would probably stay in the cache all the
time. The frequently used pages stand at alert, ready to be processed. This saves the
XSQL page processor the trouble of loading into memory the most active XSQL pages
each time they are called. By using a process described in the previous chapter, you can
configure how many pages you want the XSQL page cache to hold. In small sites, you
can expect all the XSQL pages to be easily loaded into the cache. If you have perfor-
mance problems, you may want to bump up the size of the cache. The trade-off is that
you will have less memory available for other tasks.
XSQL Architecture 63
In concept, the stylesheet cache is similar to the XSQL page cache. It caches
stylesheets so that the XSQL page processor doesn’t have to fetch them from the file sys-
tem every time. Like the XSQL page cache, it uses an LRU algorithm to determine which
stylesheets most greatly need to be in the cache. However, it works quite a bit differently
than the XSQL page cache. It is optimized for multithreaded servlet engines.
Why would the two need to work differently? The acts of XSQL page processing and
stylesheet processing are quite different. Once an XSQL page is loaded, it can be repre-
sented statically in memory. The XSQL page processor can respond to requests by
accessing instantiated objects; that is, the XSQL page itself doesn’t need to be reparsed
in any way on each request.
XSLT is more complex. An XSL stylesheet is merged with the resulting XML in a way
described by the stylesheet. As you will see in Chapter 5, the XSLT language isn’t trivial.
Since the XML is expected to change from one query to the next, there is a lot of processing
work that must occur at the time of invocation. On a multithreaded servlet engine, multi-
ple threads could easily process the same XSLT stylesheet at the same time. For caching to
be effective, it makes sense that there should be multiple instances of the cached stylesheet.
Thus, the stylesheet cache uses an LRU cache in conjunction with pooling.
The LRU cache is a cache of pools. The pool is designed to grow when multiple
threads demand the same stylesheet and to contract when activity is slower. When there
is a set amount of inactivity (the time-out seconds parameter in the XSQLConfig.xml
file), a particular stylesheet instance will drop out of the pool. In a busy servlet engine,
the individual pools can grow for popular stylesheets so that all threads can have their
own instance.
The pooling is one side of the equation; the cache itself is the other side. The LRU
cache works essentially the same as the XSQL page cache. Think of it like this: If you
configured the stylesheet pool so that it could have, at a maximum, only one stylesheet
instance, the XSQL page cache and the stylesheet cache would work exactly the same.
As you can see, the actual processing of XSQL is quite complex. This complexity,
however, is hidden from the user. Programmers could work with XSQL for years and
never have any reason to know about the details discussed here, but if they encounter
performance problems, an understanding of the underlying XSQL architecture would
be very useful.
XML Parser
In any XML-based system, sooner or later you are going to run into an XML parser.
There is no exception to this rule here. The XSQL page processor makes use of the
64 Chapter 4
Oracle XML parser in a couple of key ways. XSQL pages and XSLT stylesheets are all
XML documents, so they need to be parsed. As an XSQL developer, you may never
need to use the XML parser. However, if you wish to extend the functionality of XSQL,
you may find reason to use the parser directly. You’ll learn more about programming
with the XML parser in later chapters; here, you’ll learn about the architecture of the
Oracle XML parser.
XSQL uses the Document Object Model (DOM) functionality of the Oracle XML
parser. Like all DOM XML parsers, the Oracle XML parser takes a stream—which may
be a file, a string generated internally by an application, or data received over the
network—and creates an in-memory representation of the XML. The representation,
called a document, looks like a tree and can be traversed and queried to get data out of
it. The document can also be added to and otherwise modified. Usually, at some point
it is passed along to some other component. It might be written to the file system, sent
over the network, or just handed as an argument to another component.
XML SQL
The Oracle XML SQL utility is used by the XSQL page processor in its interaction with
the database. This is a stand-alone utility that you can use as part of the Oracle XDK.
The use of the utility in conjunction with XSQL pages is transparent. You can be an
effective XSQL developer without knowing anything about XML SQL, but for a solid
understanding of the overall architecture, it’s important that you know how XML SQL
fits in.
On queries, XML SQL is used to translate the result set into XML. It produces a
canonical XML representation, covered in detail later in this chapter. This representa-
tion is based on a standard schema that works for all SQL queries. When working with
the output of XSQL, you can assume that the results will come back in this schema.
XSQL replaces the query call with these results that XML SQL provides.
XML SQL is also used by XSQL when pushing data to the database by using the
xsql:insert-request, xsql:update-request, xsql:delete-request, and
xsql:insert-param actions. The canonical schema is used for these cases, also. In
these situations, the burden is on you to ensure that your data is in the correct format.
Doing so is easily accomplished with XSLT stylesheets, as you’ll see.
Much of XSQL is based on XML SQL. If you find yourself wanting to get under the
hood of XSQL, you’ll want to explore the Oracle XML SQL utility. As you’ll see in
Chapter 18, it is easy to exploit the power of XML SQL from your own custom action
handlers.
XSLT Processor
The XSLT processor performs the XSLT transformations. Without the XSLT processor,
XSQL is pretty boring, for all you have is an easy way to execute commands against
the database and produce XML from SQL statements. The XSLT processor takes the
result of the XSQL page processor and transforms it into something usable by the
requesting agent.
XSQL Architecture 65
As an XSQL developer, you have very little direct interaction with the XSLT proces-
sor. You tell it what to do with the stylesheet processing instruction that is documented
completely later in this chapter. The most significant part of this instruction is the loca-
tion of the stylesheet. The XSLT processor finds the stylesheet and performs the trans-
formation.
The transformation itself is dictated by the stylesheet, which is written in the XSLT
language. This language allows you to describe how you want the source XML to
merge with the text in the stylesheet. Usually, the stylesheet contains HTML markup
that describes how nodes in your XML should be output. You can, however, output
whatever you want.
The XSLT engine is compliant with the World Wide Web Consortium (W3C) XSLT
1.0 Recommendation released in 1999 and, for the moment, acting as the driving stan-
dard behind XSLT. You might run across an old stylesheet based on an earlier standard.
The processing instruction of the stylesheet should enable you to determine whether
that stylesheet is old.
XSQL Pages
These are XML documents that describe what the XSQL page processor should do.
We’ve already described several XSQL pages used for executing SQL queries, as well
as those for linking to a stylesheet that transforms the resulting XML.
First, it’s important to note that an XSQL page doesn’t have to be a file. When in
Chapter 17 you learn about embedding the XSQL page processor into your Java
code, you’ll see that you can construct your XSQL page at runtime. You can also create
an XSQL page with another XSQL page, dynamically. To do so, the XSQL page proces-
sor requires only that the XSQL page be a valid, well-formed XML document. When
XSQL is used programmatically, there is a lot of flexibility in how an XSQL page may
originate. When the XSQL servlet is used, however, your XSQL page needs to exist as
a file.
An XSQL page itself can contain several different entities. Any nontrivial XSQL page
contains one or more actions, which are discussed in the next chapter. The actions tell
the XSQL page processor what it should do. You can also link an XSQL page to an XSLT
stylesheet. Doing so tells the XSQL page processor to use the specified stylesheet to
transform the results. The XSQL page can also specify a serializer, which is discussed
in more detail later in this section. You can use a serializer to specify exactly how the
data are written to the output.
66 Chapter 4
Actions
XSQL is built around the concept of actions, an XML element that defines what you
want to do. You have already seen the xsql:query action. This action is, by far, the
most popular, but there are others.
The XSQL page processor executes the actions. It takes the resulting XML datagram
for a particular query and replaces the original action element with the XML datagram.
More than one action can be included in the same page. In the case of multiple actions
in a page, you must have a top-level element that contains all the action elements on
the page. This is an XML requirement, so if you don’t follow this requirement, the doc-
ument will not be valid XML.
The following code is the first XSQL page that you saw in Chapter 1. It contains one
action, the xsql:query action. This action is simply an XML element that belongs to
the XSQL namespace.
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl”
href=”emp-intro.xsl”?>
<page xmlns:xsql=”urn:oracle-xsql”
connection=”demo”>
<xsql:query>
SELECT ename, job, sal FROM emp
WHERE deptno=20
ORDER BY sal
</xsql:query>
</page>
In Chapter 5, you’ll learn more about built-in actions. In Chapter 18, you’ll see how
you can define your own actions.
Action Handlers
Action handlers handle XSQL actions. In the case of the built-in actions, XSQL implic-
itly relates a given action to an action handler. When the built-in actions don’t provide
you with the functionality that you desire, you can create your own custom action
handlers. The architecture of action handlers is diagrammed in Figure 4.6. The XSQL
page processor knows about the built-in action handlers; for the custom action han-
dlers, you either specify the name of the class in the XSQL page or cite it by a nickname
given in the XSQLConfig.xml file.
In Figure 4.6, the some-page.xsql has three actions, including the xsql:query
action that you’ve seen used in the code examples so far. The XSQL page processor
handles that action with the built-in action handler called the XSQL query handler.
There is a built-in action handler for each action discussed in the next chapter. The two
custom action handler classes handle the two custom actions and are loaded into the
JVM. A custom action handler can do whatever you want. In Chapter 18 you’ll learn
more about programming custom action handlers.
XSQL Architecture 67
custom action 1
XSQL Page Processor
custom action 2
some-page.xsql
Custom Action2
Built-in Actions
Serializers
Serialization is the process of writing the end XML document. In our earliest examples
in the last chapter, the default serializer wrote XML out to a network stream only, and
our browser displayed it. The serializer’s job isn’t hard: It just takes what the XSQL
page processor assembles in memory and writes it to output. Figure 4.7 shows how the
serializer fits in.
The architecture of serializers is simpler than that of action handlers. Although you
can—and often will—use multiple action handlers in your XSQL page, you can specify
only one serializer per XSQL page. In most cases, you won’t need to specify a serializer
at all.
Some Serializer
some-page.xsql
Some Serializer
There are several built-in action handlers, but the only built-in serializer is the
default one. Like action handlers, you can create your own custom serializers, a
process you’ll learn more about in Chapter 19. Why would you want to? In most cases,
you won’t need to; the default serializer will do just fine. The primary reason to specify
a serializer is to output binary data, such as a PDF or image. Oracle provides a serial-
izer that allows you to use Apache FOP to create dynamic PDFs.
Moving On
This chapter focused on the high-level architecture of XSQL. Chapter 5 discusses XSQL
coding; Chapters 6 and 7 show you the ins and outs of how XSQL works. You’ll exam-
ine the technologies associated with XSQL—first, SQL and other Oracle database tech-
nologies, then XSLT. You can use much of the material in Chapters 5 through 7 for
building upon, implicitly, the knowledge you’ve gained from this chapter. By having a
good understanding of XSQL architecture, you’ll find developing good XSQL-based
systems easy.
CHAPTER
In the previous chapter, you learned about the architecture of XSQL. You looked at a lot
of diagrams, but you did not write any code. In this chapter, you start coding. The first
step is to flesh out your knowledge of the xsql:query action, which you used in
previous examples. This is the action used to retrieve data from the database using
SQL statements. This is only one of the built-in actions, though. All of them are covered
in this chapter. But before going in to the rest of the actions, you will learn about the
canonical XSQL schema and date formatting. These two areas apply generally to
several of the built-in actions, including xsql:query. The chapter ends by describing
how to link to XSLT stylesheets.
This chapter provides all of the raw information for using the built-in XSQL actions.
A couple of the areas, however, are explored in more detail in later chapters. Passing
parameters between pages is very important, and is covered in Chapter 6. The actions
that allow you to put information into the database are covered here, but an in-depth
discussion of how to modify your database is presented in Chapter 7. You will learn
how to create your own custom actions in Chapter 18.
69
70 Chapter 5
the xsql:query tags as shown in the following example. The only requirement is that
it be a select statement. If you try to insert a statement that would modify data or
administer data objects, the XSQL page processor will reject it.
<?xml version=”1.0”?>
<xsql:query connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
SELECT * from emp
</xsql:query>
As you have observed earlier, the result set from the query is turned into an XML
fragment that replaces the xsql:query element in the output. The names of the XML
elements are determined by the query. For our particular example here, the emp table
has columns named empno, ename, job, mgr, hiredate, sal, comm, and deptno.
For each row, an XML element with the name of the column contains the data for that
column. This can be seen in Figure 5.1.
If you don’t like the names of the columns, you can alias them using SQL. For
instance, you can use SELECT ename as “employee name” so that the XML is
returned with employee_name elements instead of ename elements, as in the follow-
ing example
<?xml version=”1.0”?>
<xsql:query connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
SELECT ename as “employee_name” from emp
</xsql:query>
In some cases, you have to alias the field name. Let’s say that, in addition to the
name, you want to know the year and month. The simplest SQL for getting that infor-
mation is:
But if you put this into your XSQL page, you will get an error. The default name for
the second field contains parentheses, which are illegal characters for an XML tag
name, so you need to alias the field to something else. Here’s how you do it:
<?xml version=”1.0”?>
<xsql:query connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
SELECT ename as “employee_name”,
to_char(hiredate,’YYYY-MON’) AS “hiredate”
FROM emp
</xsql:query>
This XSQL page will produce the result shown in Figure 5.2.
You will remember from the previous discussion on XML that there are several ille-
gal characters for XML tag names. This means that you cannot arbitrarily alias the field
names to anything you want. A common mistake is to try to alias a field to a name that
contains a space. This will generate an error message.
N OT E Unless you are a SQL guru, you probably have a lot of questions about
specific types of SQL queries. Chapter 8 covers all of SQL from an XSQL
perspective. Stay tuned!
You can also control the row and rowset element names. You do this by setting the
row-element and rowset-element attributes of xsql:query. Here’s how it
works:
<?xml version=”1.0”?>
<xsql:query connection=”demo”
xmlns:xsql=”urn:oracle-xsql”
row-element=”EMPLOYEES”
rowset-element=”EMPLOYEE_LIST”>
SELECT * FROM emp
</xsql:query>
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
select dname from dept where deptno=10
</xsql:query>
<xsql:query>
select ename from emp where deptno=10
</xsql:query>
</page>
When you look at this example, you should see something similar to Figure 5.4. In this
screenshot, the operator has scrolled down to where the first query ends and the sec-
ond query begins.
In this case it is easy to see, because the two queries pulled from different tables. But
what if you had two different queries in the same page that pulled the same fields from
the same table? It would be hard to distinguish where one query ended and the other
began. Instead, you should enclose each query inside a distinct element, as follows:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<dept-query>
<xsql:query>
select dname from dept where deptno=10
</xsql:query>
</dept-query>
<emp-query>
<xsql:query>
select ename from emp where deptno=10
</xsql:query>
</emp-query>
</page>
xsql:query Details
The xsql:query element has several available attributes. These attributes control
how the action behaves. In the coming chapters, you will make use of many of these
attributes to access functionality. Here, the attributes are grouped together by the kind
of effect they have. The first group includes the XML control attributes. These attrib-
utes control how the XML is created, including the date format, the case of the tag
names, the presentation of null values, and the grouping tag names provided by
default by XSQL. These are all outlined in Table 5.1.
ATTRIBUTE DESCRIPTION
NAME DESCRIPTION
skip-rows Number of rows that to skip before fetching and writing rows
from the query in to the XML.
The next group of xsql:query attributes helps with the pagination of a large result
set. These attributes are described in Table 5.2. You will see more on how to use them
to create stateless paging in Chapter 14.
Table 5.3, creatively named “Miscellaneous Attributes,” lists the last group of attrib-
utes. These attributes couldn’t be grouped together any other way.
The xsql:query element is one of the key elements for all of XSQL. Hopefully, this
section gives you a good basis and can act as a good reference. You will be using the
xsql:query element, and the lessons learned here, throughout the rest of the book.
xsql:no-rows-query
The xsql:no-rows-query element is an element that can only be used as a child of
xsql:query. It helps with a common situation—your query returns no rows. If your
query does not match any rows, then you are not going to get any XML back. If all you
want is to display a message such as “No data” when this situation occurs, XSLT can eas-
ily accomplish this for you. However, you may want to use an entirely different query.
In such a case, you simply place an xsql:no-rows-query element inside the
xsql:query element as follows. In case your no-rows-query statement also fails to
return rows, you can nest as many xsql:no-row-query elements inside of an
xsql:no-row-query element as you need.
<xsql:query>
SELECT statement
<xsql:no-rows-query>
SELECT statement to use in case of no rows
</xsql:no-rows-query>
</xsql:query>
Canonical Schema
In interacting with the database, XSQL solves a couple of basic problems. It converts SQL
results to XML, and it converts XML to SQL. You’ve seen the conversion of SQL results
to XML already with the xsql:query action, and you’ll see the XML-to-SQL conversion
later in this chapter. In both of these cases, data that is usually arranged in a table has to
be represented in a tree format. Oracle solves this problem with a canonical schema. This
schema is used by xsql:query when result sets are presented as XML and is required
by the XSQL actions that insert XML documents into database tables as relational data.
The schema uses the following common format for queries and XML insertions,
updates, and deletions. In all these cases, you use XSLT transformations to massage the
data. For queries, you use an XSLT stylesheet to transform the data to a format that is
appropriate. Most often, this means transforming the canonical representation into
HTML. When pushing data to the database, you use a stylesheet to transform the data
from its original format into the canonical form.
The canonical form can be used to represent all of the Oracle datatypes, including
cursors, objects, and collections. But first, say that you have a simple table in the data-
base, which includes the values in Table 5.4.
78 Chapter 5
COLUMN_NAME_1 COLUMN_NAME_2
Value1.1 Value1.2
Value2.1 Value2.2
When you select all of the rows from the table, the canonical representation will
appear as follows.
<ROWSET>
<ROW id=”1”>
<COLUMN_NAME_1>Value1.1</COLUMN_NAME_1>
<COLUMN_NAME_2>Value1.2</COLUMN_NAME_2>
</ROW>
<ROW id=”2”>
<COLUMN_NAME_1>Value2.1</COLUMN_NAME_1>
<COLUMN_NAME_2>Value2.2</COLUMN_NAME_2>
</ROW>
</ROWSET>
If you add more rows, you will get additional row elements. If you add more
columns, you will have additional elements inside each row. The general pattern is that
each row is an element that contains each field in that row as child element. In this case,
you can assume that the datatypes are strings. In fact, it doesn’t matter. Numbers will
appear the same way without any signifying formatting. In the canonical form, every-
thing is text. This is also true for dates, although you control how dates are presented
and interpreted with the date formats described later in this chapter. As you learned
before, you can change the rowset and row element names when using xsql:query.
But even when these are changed, you have not changed the overall structure of the
result datagram.
If you’ve been around SQL for a while, you know that not all queries produce such
a simple output. You can have cursors that create their own tree structures. In such a
case, the form looks like the following example:
<ROWSET>
<ROW id=”1”>
Writing XSQL Pages 79
<REGULAR_COLUMN>Regular Value</REGULAR_COLUMN>
<YOUR_CURSOR_NAME>
<YOUR_CURSOR_NAME_ROW id=”1”>
<FIELD1>Value For Field #1 of the Cursor</FIELD1>
<FIELD2>Value For Field #2 of the Cursor</FIELD2>
</YOUR_CURSOR_NAME_ROW>
</YOUR_CURSOR_NAME>
</ROW>
</ROWSET>
As you can see, cursors work quite naturally with the tree structure of XML. The fol-
lowing real-world example demonstrates this more clearly. Here is a SQL query that
you can run as the scott/tiger user that utilizes cursors. If you aren’t familiar with
cursors, don’t worry—they are covered in Chapter 8.
The result of the first two rows of this canonical query is as follows:
<ROWSET>
<ROW num=”1”>
<DNAME>ACCOUNTING</DNAME>
<EMPLOYEES>
<EMPLOYEES_ROW num=”1”>
<ENAME>CLARK</ENAME>
</EMPLOYEES_ROW>
<EMPLOYEES_ROW num=”2”>
<ENAME>KING</ENAME>
</EMPLOYEES_ROW>
<EMPLOYEES_ROW num=”3”>
<ENAME>MILLER</ENAME>
</EMPLOYEES_ROW>
</EMPLOYEES>
</ROW>
<ROW num=”2”>
<DNAME>RESEARCH</DNAME>
<EMPLOYEES>
<EMPLOYEES_ROW num=”1”>
<ENAME>SMITH</ENAME>
</EMPLOYEES_ROW>
<EMPLOYEES_ROW num=”2”>
<ENAME>JONES</ENAME>
</EMPLOYEES_ROW>
<EMPLOYEES_ROW num=”3”>
<ENAME>SCOTT</ENAME>
</EMPLOYEES_ROW>
<EMPLOYEES_ROW num=”4”>
<ENAME>ADAMS</ENAME>
80 Chapter 5
</EMPLOYEES_ROW>
</EMPLOYEES>
</ROW>
</ROWSET>
Oracle objects and collections also appear as nested structures in the canonical rep-
resentation. The following example shows a single row with an object and a collection:
<ROWSET>
<ROW num=”1”>
<YOUR_OBJECT_COLUMN_NAME>
<ATTRIBUTE1>Value1</ATTRIBUTE1>
<ATTRIBUTE2>Value2</ATTRIBUTE2>
</YOUR_OBJECT_COLUMN_NAME>
<YOUR_COLLECTION_NAME>
<YOUR_COLLECTION_NAME_ITEM>
<ATTRIBUTE1>Value1</ATTRIBUTE1>
<ATTRIBUTE2>Value2</ATTRIBUTE2>
</YOUR_COLLECTION_NAME_ITEM>
<YOUR_COLLECTION_NAME_ITEM>
<ATTRIBUTE1>Value1</ATTRIBUTE1>
<ATTRIBUTE2>Value2</ATTRIBUTE2>
</YOUR_COLLECTION_NAME_ITEM>
</YOUR_COLLECTION_NAME>
</ROW>
</ROWSET>
Formatting Dates
Because most applications use dates at some point, it is very important to understand
how XSQL works with dates. XSQL uses date formats that act as masks. A date format
is a combination of different symbols that represent different parts of the date-time
stamp. XSQL uses date formats both when retrieving information from the database
and when putting information into it. When retrieving information, XSQL writes the
date to output in accordance with the date format. When inputting information, it
assumes that dates are in the format specified by the date format.
The characters that represent different parts of the date format are listed in Table 5.5.
Padding for variable-length data can be supplied by repeating the symbol. For exam-
ple, if you want 08 to represent 8 A.M., you put hh in the format string.
G Era designator AD
y Year 1996
D Day in month 10
m Minute in hour 30
s Second in minute 55
S Millisecond 978
W Week in year 27
W Week in month 2
a A.M./P.M. marker PM
‘’ Single quote ‘
FORMAT EXAMPLE
Several of the XSQL built-in actions use date formats. All of them use the same format.
xsql:dml
DML stands for Data Manipulation Language. It means that you are going to change
something in the database. The xsql:dml element is used for statements of change,
including insertions, updates, deletions, and even creation and deletion of database
objects. Though you should heed the warning against actually deleting whole tables,
the xsql:dml element is very useful for processing input to your database. You can
also use it to execute PL/SQL blocks.
The following example uses a PL/SQL block to insert two records into the database
and commit them. As you will learn in Chapter 9, PL/SQL can be used to do a great
variety of tasks. You can call a wide variety of procedures, use conditional logic, and
even process results. The example here is very pedestrian, but you will see more com-
plex examples in Chapter 9.
When you look at the results of the example, you will see that there is little result that
comes back from the xsql:dml tag. All that you can expect is either an error message if
something goes wrong or an element that tells you the number of rows that are affected.
You will learn in Chapter 14 how best to return to the user the status of a DML request
There are a couple of attributes that you can use to configure the behavior of the
xsql:dml element. They are described in Table 5.7.
Writing XSQL Pages 83
xsql:ref-cursor-function
This action interacts with a PL/SQL function. A PL/SQL function returns a value at the
end of execution. Some PL/SQL functions return cursors, acting essentially like a result
set. The difference is that the PL/SQL function can assemble the cursor by using condi-
tional logic and several SQL statements across a variety of tables. Only PL/SQL functions
that return the type REF CURSOR can be called from a xsql:ref-cursor-function
action.
Assume that you have a function whose name is MyPackage.MyFunction and
that it returns a REF CURSOR. You would call it as follows:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<ref-cursor>
<xsql:ref-cursor-function>
MyPackage.MyFunction(1)
</xsql:ref-cursor-function>
</ref-cursor>
</page>
This action works very similarly to the xsql:query action, except that it calls a
PL/SQL function instead of issuing SQL statements directly. It is little surprise, then,
that almost all of the attributes for xsql:query work for xsql:ref-cursor
-function. There is only one exception—the fetch-size attribute can’t be applied here.
You will learn more about using PL/SQL in Chapter 9.
xsql:include-owa
It may be the case that you have stored procedures in the database that generate XML.
It’s quite reasonable that you might want to reuse that logic and have that XML
included in your XSQL page. This is the purpose of the xsql:include-owa action.
There are two standard Oracle Web Agent (OWA) packages (HTP and HTF) that will
84 Chapter 5
write to the server-side page buffer. When this action is used, the XSQL page processor
executes the PL/SQL block included in the action, assuming that the functions and pro-
cedures of the HTP and HTF are being used to write XML to the server-side page buffer.
The XSQL page processor then grabs the output out of the page buffer and inserts it in
to the outgoing XSQL page. It is the responsibility of the PL/SQL programmer to ensure
that the XML is well formed. Here is an example of how to use this action:
<xsql:include-owa>
PL/SQL Block using HTP and/or HTF packages to write to server-side page
buffer
</xsql:include-owa>
There are a couple of attributes that can be used with the xsql:include-owa
action. They are described in Table 5.8.
xsql:include-request-params
This action is a utility action designed to make it easier to write XSLT stylesheets. It
formats every parameter and its value to XML, including not only form- and URL-
based parameters but also session parameters and cookies. The usage is very simple,
and there are no optional attributes. Just put the following into your XSQL document:
<xsql:include-request-params/>
In its place you will get XML back in the following form. If there are no session
variables or cookies, those elements will not occur.
<request>
<parameters>
<ParamName1>value1</ParamName1>
<ParamName2>value2</ParamName2>
</parameters>
<session>
<SessionVarName1>value1</SessionVarName1>
<SessionVarName2>value2</SessionVarName2>
</session>
<cookies>
<cookieName1>value1</cookieName1>
<cookieName2>value2</cookeName2>
</cookies>
</request>
When you look at XSLT in depth in Chapter 13, you will learn more about the use-
fulness of this action.
Writing XSQL Pages 85
ATTRIBUTE DESCRIPTION
xsql:include-param
This action allows you to include a single parameter in to your XML datagram. It is
essentially a scaled back, specific version of the xsql:include-request-params
action. It gives you an easy way to make your parameters and their values available to
your XSLT stylesheets. Here is the syntax:
<xsql:include-param name=”paramname”/>
xsql:include-xml
This action allows you to include XML in your datagram from any URL. Relative URLs
will point to files in your local Web space, whereas absolute URLs can grab the
resource from anywhere on the Web. The XML retrieved must be well formed. Here is
how you use it:
<xsql:include-xml href=”URL”/>
xsql:set-page-param
The xsql:set-page-param action allows you to set a page-private parameter for
the XSQL page. You can set the parameter either as text, as text and other parameter
values, or as the result of a SQL statement. There are two different syntaxes for this
attribute. The first syntax covers the first two cases:
This will set paramname=value. You may be wondering how you would use this
syntax to assign the value of another parameter. You will learn more about this later in
the chapter, when you read about passing parameters. For now, here is one example:
<xsql:set-page-param name=”sql-based-param”>
SQL statement
</xsql:set-page-param>
This action has a couple of attributes, detailed in Table 5.9. The name attribute is
required.
xsql:set-session-param
The xsql:set-session-param action allows you to set a parameter on the current
browser user’s HTTP session. This session is controlled by the Web server, but it gen-
erally ends after a time out or when the browser is closed. This action has an effect only
when you are using XSQL in conjunction with Java servlets. Nothing happens if the
XSQL request object or XSQL command line encounters this action in a page that it is
processing.
The syntax behaves very much like the syntax for the xsql:set-page-param
action. You can set a parameter’s value in two ways. You can set it directly, as follows:
You can also set the parameter’s value by issuing a SQL statement. The value
assigned is that of the first column of the first row. For clarity, it is best to structure your
SQL statements so that only one row and one column will be returned.
<xsql:set-session-param name=”sql-based-param”>
SQL statement
</xsql:set-session-param>
xsql:set-cookie
This action sets an HTTP cookie to a specified value. This action has an effect only
when you are using XSQL in conjunction with Java servlets. Nothing happens if the
XSQL request object or XSQL command line encounters this action in a page that it is
processing.
The syntax behaves very much like the syntax for the xsql:set-page-param
action. You can set a parameter’s value in two ways. You can set it directly, as follows:
You can also set the parameter’s value by issuing a SQL statement. The value
assigned is that of the first column of the first row. For clarity, it is best to structure your
SQL statements so that only one row and one column will be returned.
<xsql:set-cookie name=”sql-based-param”>
SQL statement
</xsql:set-cookie>
The xsql:set-cookie action has several attributes, listed in Table 5.11. The name
attribute is required.
xsql:set-stylesheet-param
This action allows you to dynamically set a top-level stylesheet parameter that the
XSQL page processor should pass on to the XSLT stylesheet. You will learn more about
XSLT stylesheet parameters in Chapter 13.
The syntax is similar to that of the xsql:set-page-param, xsql:set
-session-param, and xsql:set-cookie actions. To set the stylesheet to a particu-
lar value, including possibly another parameter, use:
You can also set the parameter’s value by issuing a SQL statement. The value
assigned is that of the first column of the first row. For clarity, it is best to structure your
SQL statements so that only one row and one column will be returned.
<xsql:set-stylesheet-param name=”sql-based-param”>
SQL statement
</xsql:set-stylesheet-param>
xsql:action
The xsql:action element is used to enable custom action handlers. When this action
is processed, a custom action handler of your choosing is invoked. You will learn more
about how to write custom action handlers in Chapter 17. The only requirement is that
the xsql:action element have a handler attribute. Beyond that, it is restricted only
by the XML syntax for all elements and the requirements of the custom action handler.
The custom action handler will consume the containing elements and parameters.
As you will learn in Chapter 17, it is very important that custom action handler devel-
opers thoroughly document what their action handlers require and can optionally use.
xsql:include-xsql
The xsql:include-xsql action can be used to include the results of other XSQL
pages in the current XSQL page. This can make your XSQL pages more reusable. The
xsql:include-xsql action is used as follows:
<xsql:include-xsql href=”other.xsql”/>
The results of the other XSQL page will be included wherever this element is placed
in the XSQL document. If the XSQL document is tied to a stylesheet, the stylesheet will
be applied before inclusion. The results can also be reparsed. All of the request and ses-
sion parameters available to the including page will be available to the included page.
Table 5.13 lists the optional attributes.
90 Chapter 5
xsql:insert-request
This action allows you to insert an XML document into the database. There are two
ways that data can be inserted: An XML document received over HTTP can be inserted,
or the parameters for the XSQL page, including possibly HTML form parameters, can
be inserted. In the first case, an XML document is received in the body of a HTTP post
request. Either the XML document must be in the canonical ROWSET/ROW format, or a
stylesheet must be specified to translate the XML document to the canonical form.
In the second case, the parameters included in a request can be inserted. These can
include cookies, session parameters, and request parameters. The most common case
involves inserting data from HTML forms. If you wish to insert HTML form data, the
XSQL page processor will translate the form data to XML, using the xsql:include
-request-params action. You then translate this XML format to the canonical format
with your own XSLT stylesheet.
The use of this action in the insertion of data is further discussed in Chapter 7. The
syntax of this action is as follows:
<xsql:insert-request table=”emp”/>
Table 5.14 lists the attributes that can be used in conjunction with this action.
xsql:update-request
This action allows you to update an XML document against the database. There are
two ways that data can be updated: An XML document received over HTTP can be
updated, or the parameters for the XSQL page, including possibly HTML form para-
meters, can be updated. In the first case, an XML document is received in the body of a
HTTP post request. Either the XML document must be in the canonical ROWSET/ROW
format, or a stylesheet must be specified to translate the XML document to the canoni-
cal form.
In the second case, the parameters included in a request can be updated. These can
include cookies, session parameters, and request parameters. The most common case
involves updating rows based on data from HTML forms. If you wish to do this, the
XSQL page processor will translate the form data to XML using the xsql:include
-request-params action. You then translate this XML format to the canonical format
with your own XSLT stylesheet.
The use of this action in the insertion of data is further discussed in Chapter 7. The
syntax of this action is as follows:
Table 5.15 lists the attributes that can be used in conjunction with this action.
(continues)
92 Chapter 5
xsql:delete-request
This action allows you to delete data from the database base on an XML document.
There are two ways that data can be deleted: An XML document received over HTTP
can define the deletion, or the parameters for the XSQL page, including possibly
HTML form parameters, can define the deletion. In the first case, an XML document is
received in the body of a HTTP post request. Either the XML document must be in the
canonical ROWSET/ROW format, or a stylesheet must be specified to translate the XML
document to the canonical form.
In the second case, the parameters included in a request can define the deletion. These
can include cookies, session parameters, and request parameters. The most common case
involves data from HTML forms. If you wish to go this route, the XSQL page processor
will translate the form data to XML using the xsql:include-request-params
action. You then translate this XML format to the canonical format with your own XSLT
stylesheet.
The use of this action in the deletion of data is further discussed in Chapter 7. The
syntax of this action is as follows:
Table 5.16 lists the attributes that can be used in conjunction with this action.
Writing XSQL Pages 93
xsql:insert-param
The insert-param action is used to interpret the value of a particular parameter as a
XML document and insert it into the database. The parameter arrives as part of an
HTTP request. The syntax is as follows:
(continues)
94 Chapter 5
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”emp.xsl”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
SELECT * FROM EMP
</xsql:query>
</page
By default, the XSLT stylesheet is processed on the server. If you want to defer the pro-
cessing to the client, you can do this by setting the client attribute to yes. Some Web
browsers, including Internet Explorer 5.0 and 6.0, contain XSLT processors and can per-
form the transformation at the client. However, it is important to know that you introduce
browser-compatibility issues when you defer to the client. In fact, even IE 5.0 and IE 6.0
Writing XSQL Pages 95
transform stylesheets differently. The most appropriate reason to defer processing to the
client is to offload processing to the clients. Even then, you should do it only if you can
mandate what XSLT processors exist on the client, or be prepared to deal with different
flavors of processors. Here is an example of deferring the transformation to the client:
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”emp.xsl” client=”yes” ?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
SELECT * FROM EMP
</xsql:query>
</page>
NAME DESCRIPTION
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” media=”lynx” href=”lynx-emp.xsl”
client=”yes” ?>
<?xml-stylesheet type=”text/xsl” media=”msie 5” href=”msie-emp.xsl”
client=”yes” ?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
SELECT * FROM EMP
</xsql:query>
</page>
Moving On
Now you have a working knowledge of all of the XSQL actions. Still, you probably
have some unanswered questions about the specifics. The next two chapters delve
more deeply into the XSQL parameters and database modification. This will get you
squared away with XSQL and then you will be ready to tackle the Oracle database
technologies and XSLT. The focus returns to actions in Chapter 18, where you will learn
how to create your own custom actions.
CHAPTER
XSQL Parameters
97
98 Chapter 6
WA R N I N G If you are using XSQL from the command line or from your own
program, session and cookie parameters won’t work. Even if you are using
XSQL Servlet, session parameters and cookies aren’t guaranteed: Session
parameters depend on the configuration of the servlet engine; cookies can only
be set if the browser allows them.
Here you’ll see all the different types of parameters in action. Your first steps are to
learn how to reference parameters and how parameters can be represented in XML.
Next, you’ll look at page-private parameters, session parameters, followed by cookies.
Then, you’ll learn how XSQL references all of the different types of parameters and
resolves conflicts. From here you are ready to learn how to make your SQL queries
more efficient with JDBC bind parameters. Finally, you’ll look at integrating your para-
meters with XSLT stylesheets.
Referencing Parameters
The XSQL page processor parses XSQL pages for parameters by looking for an @
enclosed with curly brackets. When it sees this inside an XSQL page, it will replace the
token with the parameter value. This works the same regardless of how the page is
invoked. Here, you’ll learn how to reference parameters when the HTTP GET and
HTTP POST methods are used to invoke the XSQL servlet. The other kinds of invoca-
tion—XSQL command line and XSQL request—will be covered later in Chapters 7 and
8, respectively.
The following XSQL page shows examples of parameter references. This example
allows you to perform essentially any SQL select statement on any configured data-
base. Be sure to save this file as very-unsecure.xsql—you don’t want to run it on
a production server!
<?xml version=”1.0”?>
<page xmlns:xsql=”urn:oracle-xsql” connection=”{@conn}”>
<xsql:query>
select {@fields} from {@tables}
</xsql:query>
</page>
If you call just the URL by itself, you will get an error message. However, once you
put the conn, fields, and tables parameters in the query string, you’ll get a result
from your database. This URL will fetch all fields and all rows from the emp table
that is under the scott user http://localhost/xsql/ref-params.xsql?conn
=demo&fields=*&tables=emp&.
Figure 6.1 shows the result that you will get for this URI.
XSQL Parameters 99
Parameters aren’t replaced outside of the XSQL actions. If you modify your XSQL
document as shown in the following code, you won’t get the values inside the <conn>,
<fields>, and <tables> tags. Instead, you’ll just get the literal text that you put in.
<?xml version=”1.0”?>
<page xmlns:xsql=”urn:oracle-xsql” connection=”{@conn}”>
<xsql:query>
select {@fields} from {@tables}
</xsql:query>
<conn>{@conn}</conn>
<fields>{@fields}</fields>
<tables>{@tables}</tables>
</page>
Though it would be nice to have this kind of functionality, XSQL has another way to
achieve the same result, which is discussed in the next section. You can use the
<xsql:include-param> action.
100 Chapter 6
<?xml version=”1.0”?>
<page xmlns:xsql=”urn:oracle-xsql”>
<xsql:include-param name=”conn” />
Now save this as include-param.xsql. You can either pass the parameters in on
the URL as before or change the action of your very-unsecure.html form to
include-params.xsql. In either case, this page should produce the result shown in
Figure 6.2.
In this case, you have to specify the parameters one at a time. If you know that you
want all of the parameters—or, if you want so many parameters that it’s easier to just
grab all of them—then you can use the <xsql:include-request params> action.
You can create the following XSQL page as include-request-params.xsql. With
only one line of code, you’ll have every parameter:
<?xml version=”1.0”?>
<page xmlns:xsql=”urn:oracle-xsql”>
<xsql:include-request-params />
</page>
The result for this, when you use the same parameters as before, is shown in Figure 6.3.
The XML schema is very simple. Even if there are no parameters at all, you’ll always
have the following skeleton:
<request>
<parameters>
</parameters>
<session>
</session>
<cookies>
</cookies>
</request>
The parameters are children of the respective elements. As you can see in the
previous example, parameters received from forms are children of the <parameters>
element. For each parameter, a child element with the same name as the parameter
encloses the parameter’s value. This works the same for session parameters and cook-
ies. Here is an example where two of each are set:
<request>
<parameters>
<param1>param-value1</param1>
<param2>param-value2</param2>
</parameters>
<session>
<session1>session-value1</session1>
<session2>session-value2</session2>
</session>
<cookies>
<cookies1>cookies-value1</cookies1>
<cookies2>cookies-value2</cookies2>
</cookies>
</request>
Parameter Types
There are several different types of parameters in XSQL. So far, you’ve been dealing
with request parameters. This section covers the other types of parameters. A quick
definition of request parameters is given first. Next, page-private parameters are cov-
ered. These are parameters that are defined inside of a page. Last, session and cookie
parameters are covered. These can only be used in conjunction with the XSQL servlet.
Request Parameters
In the previous examples, you used request parameters. Request parameters come in
as part of the request. When you are using XSQL in conjunction with the XSQL servlet,
they are passed as either an HTTP GET or POST request. If you are using XSQL from the
command line or it is embedded in your code, you can also pass in request parameters.
Exactly how you do this is a bit beyond the scope of this chapter, but you’ll learn how
to do this in Chapters 15 and 17, respectively.
For now, you should focus on how request parameters are used with HTTP. You did
this in the earlier example when you accessed the URL http://localhost
/xsql/ref-params.xsql?conn=demo&fields=ename&tables=emp&.
In this case you are using an HTTP GET method to query your data. The GET method
is the most common HTTP method—it is what you use most any time that you click on
a link. The second most common method is POST—this is what your HTML forms usu-
ally use. You can reference parameters in XSQL with both GET and POST. For this
example, let’s say that you want to create a simple form for processing queries. Here is
the HTML for the form.
XSQL Parameters 103
<html>
<body>
<h1>Sample Form </h1>
<form action=”ref-params.xsql” method=”post”>
<table border=”0”>
<tr>
<td>Connection:</td>
<td><input type=”text” name=”conn” /></td>
</tr>
<tr>
<td>Fields:</td>
<td><input type=”text” name=”fields” /></td>
</tr>
<tr>
<td>Tables:</td>
<td><input type=”text” name=”tables” /></td>
</tr>
<tr>
<td colspan=”2” align=”center”>
<input name=”submitButton” type=”submit” value=”Select” />
</td>
</tr>
</table>
</form>
</body>
</html>
You can now do essentially any Select statement with this form. Figure 6.4 shows
how to fill out the form so you can query on just the employee names from the emp table.
When you click Select, you should get the results shown in Figure 6.4. You would
get the exact same results with the URI specified earlier and shown in Figure 6.1.
Page-Private Parameters
You can think of page-private parameters as being local to the page, whereas session
parameters and cookie parameters can span multiple page calls. Page-private parame-
ters aren’t limited solely to HTTP transactions. The XSQL command line and XSQL
request object can also provide page-private parameters. The important characteristic
of page-private parameters is that they are gone after the result is returned. Subsequent
transactions won’t know about the parameters unless they are explicitly passed.
Sometimes, you may wish to set page-private parameters explicitly in your XSQL
page. You do this with the <xsql:set-page-param> action. It allows you two ways
to set parameters. The first way equates a parameter with a string value, whereas the
second way allows you to set a parameter based on a SQL query. Examples of both
ways to set parameters follow:
<?xml version=”1.0”?>
<page xmlns:xsql=”urn:oracle-xsql” connection=”demo”>
<xsql:set-page-param name=”ename” value=”ADAMS” />
<xsql:set-page-param name=”deptno”>
select deptno from emp where ename=’{@ename}’
</xsql:set-page-param>
<xsql:include-param name=”ename” />
<xsql:include-param name=”deptno” />
<xsql:query>
select * from emp where deptno={@deptno}
</xsql:query>
</page>
Session Parameters
So far, all of our examples about parameters have been of page-private parameters.
These parameters function much like parameters to scripts. Session parameters, on the
other hand, exist across invocations of XSQL. Your servlet engine maintains the session
on behalf of the XSQL servlet. This has the implication that only servlets will be able to
make use of session parameters.
Setting session parameters is quite simple. You set them just like you set cookie
parameters. The following XSQL will set a session parameter named ename and one
named deptno:
<?xml version=”1.0”?>
<page xmlns:xsql=”urn:oracle-xsql” connection=”demo”>
<xsql:set-session-param name=”ename” value=”{@r-p-ename}” />
<xsql:set-session-param name=”deptno”>
select deptno from emp where ename=’{@ename}’
</xsql:set-session-param>
</page>
XSQL Parameters 105
Now, save the file as set-session-param.xsql, and access it with the URL
http://localhost/xsql/momnpup/set-session-param.xsql?r-p-ename
=ADAMS&.
If you now go back to the include-request-params.xsql page that you cre-
ated earlier, you’ll see the output that is shown in Figure 6.5. These parameters will be
part of your session until the session ends. Your servlet engine ultimately controls the
session itself. It usually continues until the browser window is closed or until some
period of inactivity.
If you know that a given XSQL page will only be called within a session, then you
can use session parameters instead of having to deal with the complexities of request
parameters or page-private parameters. For instance, let’s suppose that you want var-
ious kinds of information about a particular employee. You can create a simple form
that has an input field named r-p-ename and then set its action to the set-
session-param.xsql page you created previously. Alternatively, you can just use
the URL that you just used.
Now, the ename and the deptno parameters are set for the entire session. If
you want a list of everyone in the department, you can do that with a very simple
XSQL page:
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” ?>
<page xmlns:xsql=”urn:oracle-xsql” connection=”demo”>
<xsql:query>
select * from emp where deptno={@deptno}
</xsql:query>
</page>
Cookies
HTTP cookies try to solve a fundamental problem of HTTP. How do you create state
when using a stateless protocol? You saw cookies at work in the previous section. If
you look back at Figure 6.5, you’ll see an entry in the cookies element. This cookie is
actually the one that is used by the servlet engine to control the session.
The basic mechanism is that a cookie is set on the Web browser. The cookie itself is
really just a string. The Web browser stores that cookie in some way on the local
machine. That cookie is passed back on subsequent requests. The browser will only
pass a cookie back on requests for which the cookie was specifically meant. This means
that if www.yahoo.com sets a cookie, there is no way that the cookie will be passed to
your Web application, assuming that your Web application isn’t running in the
yahoo.com domain. Further, you can restrict cookies so that they are only sent back to
certain URLs within your own domain. Cookies can be either session based (i.e., they
are erased when the Web browser closes down) or long lived. Long-lived cookies can
stay on the browser virtually forever.
When the cookies return, your application is able to assume that the current transac-
tion is originating from the same Web browser as previous transactions. Thus, you are
able to maintain state on the server side. This is exactly what the servlet engine does
with the cookie that it sets. It attaches all of the session information to that particular
cookie on the server side. That single cookie can act as a key to a lot of different infor-
mation. When you set cookies manually, you can create the same kind of architecture.
This is the way cookies generally work. However, it is very important to understand
that the individual users ultimately have control of whether cookies will work this way
for them. Users can set their browser so that they don’t accept cookies, or don’t accept
long-lived cookies. They can go in and choose to delete cookies that your application
uses. If you depend on cookies for authentication, you may find your users trading
their cookies around. When using cookies, it’s important to remember that you can’t
depend fully on their full and appropriate use. Cookies certainly make the developer’s
life easier. If you can require your users to have cookies enabled on their browser, then
you’ll have an easier time designing your application. However, if you can’t, you need
to consider ways that your application will degrade gracefully.
XSQL Parameters 107
With an understanding of the problems of cookies, let’s look at how to use cookies
with XSQL. The basic model is the same as with page-private parameters and session
parameters, but with some extensions. Let’s cover the basics first. The following XSQL
page will set the employee name and the department number as cookies:
<?xml version=”1.0”?>
<page xmlns:xsql=”urn:oracle-xsql” connection=”demo”>
<xsql:set-cookie name=”ename” value=”{@r-p-ename}” />
<xsql:set-cookie name=”deptno”>
select deptno from emp where ename=’{@ename}’
</xsql:set-cookie>
<xsql:include-request-params />
</page>
An obvious question is, “Why would I want to set cookies manually instead of just
using the session?” In most cases, you wouldn’t. Cookies do have one advantage over
session parameters: You can request that cookies live past the current session. You are
able to do this with the max-age attribute as follows:
<?xml version=”1.0”?>
<page xmlns:xsql=”urn:oracle-xsql” connection=”demo”>
<xsql:set-cookie name=”long-living-cookie” max-age=”999999”
value=”To_Life” />
<xsql:set-cookie name=”deptno”>
select deptno from emp where ename=’{@ename}’
</xsql:set-cookie>
<xsql:include-request-params />
</page>
If you invoke this XSQL page, shut down your browser, and then restart it, the cookie
will survive. A call to include-request-params.xsql will confirm this. The
max-age value is in seconds, so in our example, the cookie will live for about 11 days.
You can also control the URLs to which a cookie should be returned. Previously, you
learned that your application isn’t going to receive cookies set by www.yahoo.com. In
fact, by default, no URL outside of the original directory where XSQL first set the
cookie will see it. To demonstrate this, create another directory named test-cookies
in the demo directory and copy include-request-params.xsql in it. When you
go to http://localhost/test-cookies/include-request-params.xsql,
you won’t see the long-living cookie. Go back to the original include-request
-params.xsql page, and there you will find it.
For many applications, this is too restrictive. If nothing else, it means that all of your
application must be in the same directory or subdirectories. Luckily, you can control
this behavior very easily. Using the path attribute of the xsql:set-cookie action,
you can make the cookie generally available to all of the application. This XSQL page
sets another cookie that should be available anywhere on your server:
<?xml version=”1.0”?>
<page xmlns:xsql=”urn:oracle-xsql” connection=”demo”>
<xsql:set-cookie name=”long-living-remote-cookie” value=”To_Life”
path=”/” max-age=”999999”/>
</page>
You can reference cookies just like you can any other parameter by using the
{@cookie-name} syntax. You aren’t limited solely to parameters set by XSQL. You
can reference any cookies that come your way, including cookies set by other applica-
tions in your system.
Let’s look back at our very-unsecure.xsql page. For that example, you had to
specify three parameters for the action to work at all. This is an ideal case for setting
default values. The following XSQL will accomplish this. You should save it as very
-unsecure-with-defaults.xsql.
<?xml version=”1.0”?>
<page fields=”*” conn=”demo” tables=”emp” xmlns:xsql=”urn:oracle-xsql”
connection=”{@conn}” >
<xsql:include-request-params />
<xsql:query>
select {@fields} from {@tables}
</xsql:query>
</page>
If you execute this page with no arguments, you’ll get the same results as before.
Attaching arguments will yield different results, of course, providing that the default
values are more graceful and reusable. If you only get one parameter, you don’t fail
entirely.
You can provide multiple default values for the same parameter. This is useful if you
have multiple queries in the same XSQL page. The following example sets the default
for the max attribute for all queries, while still allowing a lesser default for a couple of
queries.
<?xml version=”1.0”?>
<page max=”10” conn=”demo” xmlns:xsql=”urn:oracle-xsql”
connection=”{@conn}” >
<xsql:query>
select * from table1
</xsql:query>
<xsql:query max=”5” max-rows=”{@max}”>
select * from table2
</xsql:query>
<xsql:query>
select * from table3
</xsql:query>
<xsql:query max=”7” max-rows=”{@max}”>
select * from table4
</xsql:query>
<xsql:query>
select * from table5
</xsql:query>
</page>
but it isn’t optimal for SQL statements. Before explaining why, let’s look at how bind
variables are used. In this example, you get a list of the employees with a particular job
in a particular department.
<?xml version=”1.0”?>
<page deptno=”20” job=”CLERK” xmlns:xsql=”urn:oracle-xsql”
connection=”demo” >
<xsql:query bind-params=”deptno job”>
select * from emp where deptno=? and job=?
</xsql:query>
</page>
Instead of having the parameter reference inside of the SQL, you have question
marks. The question marks are replaced, in order, by the values of the parameters
listed in the <bind-params> attribute. Notice that you don’t have to put quotation
marks around the question mark for job. This is taken care of for you.
Bind variables make it easier to optimize SQL statements. When you don’t use bind
variables, the SQL optimizer can only optimize the statements one at a time. The
optimizer has no way to know that there are going to be lots of statements of the same
basic structure. By using bind parameters, the SQL optimizer can optimize with the
expectation that there will be more queries like the first one. In this case, it knows that
it will be looking for rows in the emp table with a particular department number and
a particular job. By using bind parameters, some optimization work of previous
queries from this page can be reused in subsequent pages.
6. Is there a default value provided in any ancestor element? (If yes, use the nearest
default value specified [e.g., a default value specified in the parent element
would be preferred over the default value specified in the grandparent element].)
7. If all answers are no and this is a bind variable, null is assigned.
8. If all answers are no and this is a lexical parameter, the empty string is
assigned.
Generally, you want to avoid the last two cases. They’ll probably cause errors for
SQL statements. Instead, you should provide default values so that your SQL state-
ments will execute. However, in some cases, you may prefer that the SQL statements
don’t execute and just toss an error. In Chapter 14, you’ll learn how to gracefully han-
dle errors from inside your stylesheets.
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”stylesheet-params.xsl”?>
<page xmlns:xsql=”urn:oracle-xsql” connection=”demo”>
<xsql:set-stylesheet-param name=”ename” value=”{@ename}” />
<xsql:set-stylesheet-param name=”deptno”>
SELECT deptno
FROM emp
WHERE ename=’{@ename}’
</xsql:set-stylesheet-param>
</page>
<xsl:param name=”ename”/>
<xsl:param name=”deptno”/>
<xsl:template match=”/page”>
<html>
<head></head>
<body>
<H1>Stylesheet Param Sampler</H1>
<p>
<b>Ename: </b><xsl:value-of select=”$ename”/>
</p>
<p>
<b>Deptno: </b><xsl:value-of select=”$deptno”/>
</p>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
This particular result was produced by setting ename equal to ADAMS as part of
the request. The following request would do it: http://localhost/xsql/momnpup
/stylesheet-params.xsql?ename=ADAMS&.
There is a lot more that you can do with stylesheet parameters than the simple
examples shown here, and you’ll learn more about them later in the book. For now,
here is a brief description of what is going on. In the stylesheet, the stylesheet parame-
ters ename and deptno are defined with the xsl:param element. The parameter
names match exactly the parameters that you set using the xsql:set-stylesheet
-param element in the XSQL page. Inside the stylesheet, the parameters are referenced
by prefixing the name with a $. Because they are specified in an xsl:value-of
element, the value of the parameter is written to the page.
From this example, you see how you can pass the parameter values directly to
your stylesheet for output. There is more that you can do with stylesheet parameters
from inside the stylesheet, but that’s for later in the book. You’ll learn all about that in
Chapter 13.
Moving On
Now that you have a strong understanding of how parameters work with XSQL, you
are well on your way to building applications with XSQL. Because your applications
almost always consist of more than one page, now you will be able to tie your XSQL
pages together. The next chapter will complete the discussion of XSQL basics with a
look at how to modify the database with XSQL. When you develop an XSQL applica-
tion in Chapter 14, you’ll again look closely at parameter passing and see how it works
with a real-world application.
CHAPTER
7
Database Modifications
with XSQL
Database-driven Web applications generally have two purposes: accessing the data in
the database and adding to or modifying the database in some way. In this chapter, you
will learn more about inputting data to the database. The actions that you will use in
this section have already been discussed in Chapter 5. The intent here is to move
beyond reference and see how the pieces can fit together.
The first step is to review the canonical schema that XSQL uses. You learned about it
in Chapter 5. Now you will be using it in a different way—to get data into the database.
Next, you will see how to input HTML forms in two different ways—by using
xsql:insert-request, xsql:update-request, and xsql:delete-request;
and by using xsql:dml. Finally, you will learn how to handle XML documents.
115
116 Chapter 7
This is not an exhaustive survey. Rather, this section attempts to further explain the
concepts behind the data modification actions covered in Chapter 5. You will first learn
about the built-in actions for inserting, updating, and deleting data and how to use them
with forms. These are good for simple cases, but become difficult to use in complex ones.
Thus, you will then learn how to use the xsql:dml action for more complex cases.
The example for both of these exercises will be a newsletter service. The example
newsletter table has columns for e-mail, name, and organization. To keep everything
simple, the table will have no constraints on it. When you begin building real applica-
tions, everything necessary for a production application will be considered. The
following SQL will create this table. You should create it under the momnpup user.
All of the examples that you see here will consist of a single SQL statement per page.
However, there is no reason that you could not have more than one per page.
<ROWSET>
<ROW>
.... <xsl:stylesheet> <html>
</ROW> .... ....
xsql:query </ROW> </xsl:stylesheet> </html>
....
</ROW>
</ROWSET> Client
(a)
<request>
<parameters> <ROWSET>
.... <ROW>
</parameters> <xsl:stylesheet> ....
</session> </ROW>
.... .... xsql:insert-request
</session> </xsl:stylesheet> </ROW>
<cookies> ....
.... </ROW> (or xsql:update-request,
</cookies>
</ROWSET> xsql:delete-request,
Client </request>
xsql:insert-param)
Parameters XSLT XSQL
Stylesheet Input
(b)
Figure 7.1 The role of the canonical schema on input versus output.
Database Modifications with XSQL 117
This is probably different from what you have seen before, so here is a full example.
Your first step is to create the HTML form. Here is a very simple one that you will use
for inserting data into the newsletter table. There are a couple of things to note about
this example. First, the action of your form is an XSQL page. You will make this XSQL
page next. Most important is the method. As you would expect, it is post. To work
with XSQL, your form must have a post method.
<html> <body>
<h1>Sign Up For Newsletter</h1>
<form action=”dummy.xsql” method=”post”>
<table border=”0”>
<tr>
<td>Name:</td> <td><input type=”text” name=”name”/></td>
</tr>
<tr>
<td>Email:</td>
<td><input type=”text” name=”email”/></td>
</tr>
<tr>
<td>Organization:</td>
<td><input type=”text” name=”org”/></td>
</tr>
<tr>
<td colspan=”2” align=”center”><input type=”submit” value=”Sign Me
Up!” /></td>
</tr>
</table>
</form>
</body>
</html>
The next step is to create the XSQL page that will insert the data. However, it is
important to understand the overall process. As you probably know, the HTTP POST
request passes the form parameters as name-value pairs. XSQL implicitly changes
those to a simple XML document. Your stylesheet than transforms the request XML
document to the canonical XML form required by the data modification actions.
For now, take one step at a time. The xsql:include-request-parameters
action shows you what the transformation will look like. The following XSQL page
will display the parameters in the XML schema that XSQL uses:
<?xml version=”1.0”?>
<xsql:include-request-params />
When you save this file as dummy.xsql in the momnpup directory alongside the
newsletter-insert.html file, it will produce the output shown in Figure 7.2. You
saw this format in Chapter 6. It is the canonical schema. As you look at the output,
notice the <parameters> element nested inside of the <request> element. Each of
your form’s parameters is represented by an element of the parameter element. Also,
notice the <cookies/> and <session/> elements. There are a couple of cookie ele-
ments, but this does not matter. They will not interfere with the insertion or modifica-
tion of the parameter data.
118 Chapter 7
<ROWSET>
<ROW>
<EMAIL>emailValue</EMAIL>
<NAME>nameValue</NAME>
<ORGANIZATION>orgValue</ORGANIZATION>
</ROW>
</ROWSET>
<xsl:value-of select=”/request/parameters/email”/>
</EMAIL>
<NAME>
<xsl:value-of select=”/request/parameters/name”/>
</NAME>
<ORGANIZATION>
<xsl:value-of select=”/request/parameters/org”/>
</ORGANIZATION>
</ROW>
</ROWSET>
Using xsql:insert-request
Your next step is to create insert-request.xsql file. Before creating it, you should
go back to the newsletter-insert.html file. The action needs to be changed from
dummy.xsql to insert-request.xsql. After that has been changed, you can make
the insert-request.xsql file as follows:
<?xml version=”1.0”?>
<page connection=”momnpup” xmlns:xsql=”urn:oracle-xsql”>
<xsql:insert-request table=”newsletter” transform=”transform-
request.xsl”/>
</page>
When you go back to newsletter-insert.html and fill out the form, you
should get the result that is shown in Figure 7.3 in your browser. If you login as
momnpup and select * from newsletter you should see the data that you
entered into your form.
Your users do not need to see the results in this form, of course. If you would just
like to say “Thanks,” you can do so with this very simple stylesheet. It hides the
raw XML.
<?xml version=”1.0”?>
<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”
version=”1.0”>
<xsl:template match=”/”>
<html>
<h1>Thanks!</h1>
</html>
</xsl:template>
</xsl:stylesheet>
To use the stylesheet, you need to add the following line as the second line in your
insert-request.xsql file:
Using xsql:update-request
The use of xsql:update-request is very similar to the use of xsql:insert
-request. The general process is exactly the same. First, you create your HTML form
and set the action to an XSQL page. When the form is submitted, the XSQL servlet will
pass the parameters as XML to the XSQL page processor. You need a stylesheet to
transform the passed XML to the canonical XML schema.
The key difference really relates to the difference between SQL inserts and updates.
An insert adds an entirely new row to your table. An update, on the other hand, mod-
ifies an existing row. To perform an update, XSQL needs to know which row (or rows)
in the database will be modified. This is done through the key-columns attribute of
the xsql:update-request element. It lists the columns that have to match for a par-
ticular row of data to be updated.
First things first. For this example, you can use the exact same form as was used for
xsql:insert-request. Assume that the e-mail address is staying the same and that
the user wants to modify the organization and name. It would be simple enough to
populate the fields in the form, but you aren’t quite ready to do that yet. For now, life
is hard for your users. Here is the modified HTML form that you should save as
newsletter-update.html:
<html>
<body>
<h1>Sign Up For Newsletter</h1>
<form action=”update-request.xsql” method=”post”>
<table border=”0”>
<tr>
Database Modifications with XSQL 121
Now you are ready to code your update-request.xsql file. It looks very much
like the insert-request.xsql file that you coded before. The difference is the
key-columns attribute. This attribute tells XSQL that you want to make changes in
any of the rows where the email field matches the e-mail address that is entered.
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”thanks.xsl” ?>
<page connection=”momnpup” xmlns:xsql=”urn:oracle-xsql”>
<xsql:update-request table=”newsletter” key-columns=”email”
transform=”transform-request.xsl”/>
</page>
After saving this file as update-request.xsql, you should get the same
“Thanks!” screen when you fill out the form of newsletter-update.html. You can
see that this XSQL page is linked to the same thanks.xsl file as the insert
-request.xsql file. There is another interesting similarity: Both XSQL pages use the
same transform-request.xsl file. You are able to do this because the names of the
form parameters are the same for both pages. Coincidence? Somewhat. The lesson to
learn here is that with a little discipline, you can save yourself some recoding.
Using xsql:delete-request
The purpose of this request is to remove data from the database. All of the steps are the
same as with the xsql:update-request. The main difference is that you only need
to submit the data that matches the key-columns that you specify. This data is used
to determine which rows should be deleted.
122 Chapter 7
Your first step is to create the HTML form. This is the same form as in the other two
examples here, except that only the email field is needed. This is the key to tell you
when to delete rows.
<html>
<body>
<h1>Sign Up For Newsletter</h1>
<form action=”delete-request.xsql” method=”post”>
<table border=”0”>
<tr>
<td>Email:</td>
<td><input type=”text” name=”email”/></td>
</tr>
<tr>
<td colspan=”2” align=”center”>
<input type=”submit” value=”Delete Me!” />
</td>
</tr>
</table>
</form>
</body>
</html>
Your next step is to create the delete-request.xsql file. It has only one differ-
ence from the update-request.xsql file. Instead of using the xsql:update
-request action, the xsql:delete-request action is used.
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”thanks.xsl” ?>
<page connection=”momnpup” xmlns:xsql=”urn:oracle-xsql”>
<xsql:delete-request table=”newsletter” key-columns=”email”
transform=”transform-request.xsl”/>
</page>
Here is the dml-insert.xsql file that you need to create. The form parameters
are interpolated directly to the SQL statement. This is your first glimpse at parameter
handling with XSQL. You will see more of this in the next section of this chapter.
<?xml version=”1.0”?>
<page connection=”momnpup” xmlns:xsql=”urn:oracle-xsql”>
<xsql:dml>
BEGIN
INSERT INTO newsletter (email,name,organization)
VALUES(‘{@email}’,’{@name}’,’{@org}’);
COMMIT;
END;
</xsql:dml>
</page>
If you are familiar with SQL, this is far simpler than creating a XSLT stylesheet to
transform the request XML. If you are not familiar with SQL, then just wait until
Chapter 8, you are sure to find the xsql:dml action to be the simpler route for your
data modification needs. To complete the example, you need to cook up a dml
-update.xsql. Here it is.
<?xml version=”1.0”?>
The last example is deletion. Again, it has the same pattern as the other examples.
<?xml version=”1.0”?>
<page connection=”momnpup” xmlns:xsql=”urn:oracle-xsql”>
<xsql:dml>
BEGIN
UPDATEnewsletter SET
email=’{@email}’,name=’{@name}’,organization’{@org}’;
COMMIT;
END;
</xsql:dml>
</page>
document containing the form parameters. In conjunction with HTML forms, their use
probably seemed a bit clumsy. However, in conjunction with Web services and
browsers capable of sending XML, their use is easier and starts to make more sense.
You will see how this works in this section. In addition to seeing how the
xsql:insert-request, xsql:update-request, and xsql:delete-request
actions work with XML documents, you will learn about the xsql:insert
-request-parameter action. This action is used when a parameter has, as its value,
an XML document.
The XSQL servlet receives the parameters and does the translation to XML. The data
is inserted, and a result is returned. That is the complete HTTP transaction. If you can
type fast enough, you can actually manually execute the exact same insertion that your
Web browser performs when you fill out the form. Just enter telnet localhost 80
and start typing!
The case you are working at here is one in which you are receiving an XML docu-
ment directly as part of the body of the HTTP POST request. In this case the transaction
looks like the preceding request. The difference is that the XML is already formatted—
the XSQL servlet doesn’t need to conjure up XML out of a list of name-value pairs. As
you will see, from here you simply have to transform the XML to the canonical format.
If you have been doing Web work for a while, you may be asking yourself: “The old
way of submitting forms is great. Why should I bother with this new way?” There are
a few reasons why posting XML in this manner is important. First, a lot of work often
goes into transforming name-value pairs to a more usable data structure. In a lot of
cases, the problem is figuring out how the different name-value pairs are related to
each other. XML is simply a more powerful way to represent data than simple name-
value pairs. The second most important reason is that a lot of Web services applications
work on this principle. The XML is passed from a machine to your application over
HTTP, and then you handle the XML. You will learn more about this in Chapter 16.
Database Modifications with XSQL 125
N OT E You have probably heard a lot about Web services and a lot about
Simple Object Access Protocol (SOAP). Per the preceding description, you can
consider any two applications communicating over HTTP to be a Web service.
They tend to exchange XML because it is the best format for the job. SOAP is a
protocol that encapsulates this basic architecture. You’ll learn more about Web
services in Chapter 16.
At present, not all Web browsers will allow you to submit XML in HTTP POST
requests. Microsoft Internet Explorer will, so it is required for these examples. The first
step is to construct the HTML form. Note that it invokes a Javascript function on
submit.
<HTML>
<BODY>
<SCRIPT>
function PostOrder (xmldoc)
{
var xmlhttp = new ActiveXObject (“Microsoft.XMLHTTP”);
xmlhttp.open(“POST”, “insert-request-xml.xsql”,false);
xmlhttp.send(xmldoc);
return xmlhttp;
}
function submitInfo(){
var xmldoc = new ActiveXObject (“Microsoft.XMLDOM”);
xmldoc.async = false;
xmldoc.loadXML(xmldocText.value);
var response = PostOrder(xmldoc);
document.write(response.responseText);
window.location.reload( false );
}
</SCRIPT>
<b>Type in an XML Document to Post:<b><br>
<TEXTAREA rows=”12” style=”width:100%” cols=”70” NAME=”xmldocText”>
<request>
<parameters>
<email>email10</email>
<name>name10</name>
<org>org10</org>
</parameters>
</request>
</TEXTAREA>
<P>
<INPUT TYPE=”button” Value=”Post XML Document”
onclick=”submitInfo()”>
</BODY>
</HTML>
126 Chapter 7
When the form is loaded in the browser, it looks like a normal form, as shown in Figure
7.4. It behaves differently, though. The Microsoft.XMLHTTP ActiveX object is created to
handle the transaction. In the present case, all of the data from the textarea element is
simply passed along. However, you aren’t limited to only text areas. You can have any
HTML form elements and then just assemble them into the XML document as you like.
For this first example, you are using the exact same XML schema that you had to use in
the preceding form examples. This makes the insert-request.xsql document very
simple. Note that it uses the exact same stylesheet as was used in the earlier example.
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”thanks.xsl” ?>
<page connection=”momnpup” xmlns:xsql=”urn:oracle-xsql”>
<xsql:insert-request table=”newsletter” transform=”insert-
request.xsl”/>
</page>
This XML schema is overkill, though. The request and parameters elements are
unnecessary for the purposes of this example. Instead, you can simplify the schema so
that it looks like the following code. (At this point, you might also want to delete or
modify the default for the text area in the HTML page.)
<newsletter-member>
<email>email value</email>
<name>name value</name>
<org>org value</org>
</newsletter-member>
Your new, simpler XML schema means that you need a new stylesheet. The
stylesheet that follows will do the trick. The only change required in the XSQL
document is the transform attribute. That should be set to xml-transform.xsl or
whatever name you choose.
From here, updates and deletions can be created with the same steps. Your XML
schema hasn’t changed, so you don’t need to create a new stylesheet. All that is needed
is to create the update-request-xml.xsql and delete-request-xml.xsql
files. Here is the update-request-xml.xsql file:
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”thanks.xsl” ?>
<page connection=”momnpup” xmlns:xsql=”urn:oracle-xsql”>
<xsql:update-request table=”newsletter” key-columns=”email”
transform=”xml-transform.xsl”/>
</page>
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”thanks.xsl” ?>
<page connection=”momnpup” xmlns:xsql=”urn:oracle-xsql”>
<xsql:delete-request table=”newsletter” key-columns=”email”
transform=”xml-transform.xsl”/>
</page>
128 Chapter 7
<HTML>
<BODY>
<b>Type in an XML Document to Post:<b>
<br>
<FORM action=”” method=”POST”>
<TEXTAREA rows=”12” style=”width:100%” cols=”70” NAME=”xmldocText”>
<ROWSET>
<ROW>
<EMAIL>email</EMAIL>
<NAME>name</NAME>
<ORGANIZATION>org</ORGANIZATION>
</ROW>
</ROWSET>
</TEXTAREA>
<P>
<INPUT TYPE=”submit” Value=”Post XML Document”>
</FORM>
</BODY>
</HTML>
The next step is to create the XSQL page. It looks exactly like the insert
-request-xml.xsql page, except that you use the xsql:insert-param action.
The only difference between the xsql:insert-param action and the xsql:insert
-request action is that you must specify the parameter element with the xsql
:insert-param action. The value of that parameter will be treated as an XML
document.
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”thanks.xsl” ?>
<page connection=”momnpup” xmlns:xsql=”urn:oracle-xsql”>
<xsql:insert-param name=”xmldocText” table=”newsletter”/>
</page>
In this example, the input is not transformed. As you can see in the default value for
xmldocText, the input is expected to be in the canonical schema format. If you expect
the data to be in a different format, you will need to transform it. You do this just as you
Database Modifications with XSQL 129
<xsql:insert-param table=”newsletter”_transform=”insert-request.xsl”/>
You probably won’t expect the user to enter an XML parameter correctly, as in this
example. Instead, you could use this action in conjunction with Javascript to assemble
one or several XML documents. Then you would use the xsql:insert-param action
to get the data into the database.
<HTML>
<BODY>
<FORM action=”” method=”POST”>
<b>Document name:</b>
<br>
<input type=”text” name=”name” />
<newsletter-member>
<email>email13</email>
<name>name13</name>
<org>org13</org>
</newsletter-member>
</TEXTAREA>
<P>
<INPUT TYPE=”submit” Value=”Post XML Document”>
</FORM>
</BODY>
</HTML>
Your parameter that has the XML is xmldocText. To insert it into the database,
just use the xsql:dml action.
130 Chapter 7
Moving On
This chapter showed you how you could move beyond just querying data in your
database. Now you can create, modify, and delete data. This competes your knowledge
of how XSQL pages work out-of-the box. From here, the book proceeds to look deeply
at the other technologies that are used in conjunction with XSQL. Oracle SQL and
related database technologies are covered first, followed by XSLT. At the end of the
book, you’ll return to XSQL to see how it can be extended with action handlers and
serializers.
CHAPTER
Oracle SQL
The key of any XSQL system is the Oracle database. This chapter covers how to access
and input data in Oracle, focusing on SQL, the predominant language for Oracle.
However, SQL isn’t procedural, so there’s a lot you can’t do with it. You fill in the gap
with PL/SQL. Oracle Text, which allows you to store, search, and retrieve text data and
XML, is also covered here. It gives you a powerful way to search text and serves as a
great basis for search engines. Chapter 11 covers the XMLType, which allows you to
store and retrieve full and partial XML documents.
SQL Syntax
This section outlines the basics of SQL syntax. Real-life examples are used to explain
select statements and make them easier to understand. This section is meant to serve as
both a quick reference and an introduction to terms that you will be learning later.
Case Sensitivity
SQL is, except for string literals, a case-insensitive language. What this means is that it
doesn’t matter whether you use SELECT or select, emp or EMP, and so on. How-
ever, anything in quotes is case-sensitive, as are the strings stored in the database.
Thus, these three statements are equivalent:
131
132 Chapter 8
If you wish to perform a case-insensitive query, you can do so with the UPPER function:
This and the other functions are covered in detail later in the chapter.
Lexical Conventions
An SQL statement is a combination of alphanumeric characters, whitespace, and the
following special characters:
* @ ! < > _ - , . ? = | $ # () + ‘ “
SELECT ename, sal, bonus, deptno FROM emp WHERE sal > 1000
String literals are enclosed in single quotes, such as ‘brother’s keeper’. If you wish to
have a single quote inside of a string literal, simply put two quotes next to each other.
Double quotes allow you to have the equivalent of string literals inside of string liter-
als that you pass to functions. They should be used with column aliases containing
spaces or special characters.
Data Types
SQL has many built-in data types, and you can also create your own. A data type is a
fixed set of properties that a value is guaranteed to have. If a column is declared with
Oracle SQL 133
a NUMBER data type, you know that you will get a number back and you are only
allowed to put a NUMBER in. The most common data types are NUMBER, VARCHAR2,
and DATE. Most data types are used to define the type of data in the columns of your
database. Also, SQL functions return their results in a particular data type, and they
can take parameters only of particular data types.
The data types can be broken into several categories: string types, number types,
date types, large-object types, and raw types. Although the purposes of the first three
are pretty obvious, the last two might not be. The large object types give you the abil-
ity to store large amounts of data—up to 4 GB. The raw types are used when you don’t
want Oracle to perform implicit character conversions. The following paragraphs
outline each of the different groups of data types.
String Types
The string types—CHAR, NCHAR, VARCHAR2, and NVARCHAR2—are used to store char-
acter data with a maximum length of either 2,000 or 4,000 bytes. Generally, VARCHAR2
is used because the strings are variable-length. CHAR and NCHAR are fixed-length and
should only be used if you know in advance that all data will be of a particular length.
Table 8.1 describes all string types.
Number Types
The number type is used to define both integer and floating-point numbers. It takes the
following form:
NUMBER(p,s)
The p argument, which is required, is the precision. It represents the number of dig-
its left of the decimal place and can range from 1 to 38. The s argument, which is
optional and defaults to 0, is the scale. It can range from (-84 to +127. A negative num-
ber given for the scale is rounded to the specified number of places to the left of the
decimal place.
The data types in Table 8.2 are also recognized so that Oracle SQL is compatible with
the American National Standards Institute (ANSI) SQL. However, all of these are
aliases to the number type.
Date Types
The date types represent dates and times. In many cases, you use them in conjunction
with the date functions discussed later in the chapter. The date types present a partic-
ular problem—unlike numbers and strings, there are many ways to format dates. To
interpret dates correctly, you use date-format masks, discussed in their own section in
this chapter. The basic DATE type is used in most cases; the other types provide for
special needs. All are described in Table 8.3.
Large-Object Types
The large-object types are used to store objects of up to 4 GB. They work great for
unstructured text documents, movies, and audio files. The internal data types are
BLOB, CLOB, and NCLOB. These data types are stored in the Oracle database itself. The
BFILE data type points to a file outside the database.
TYPE DEFINITION
INT NUMBER(38).
INTEGER NUMBER(38).
NUMERIC(p,s) NUMBER(p,s).
REAL FLOAT(63).
SMALLINT NUMBER(38).
Oracle SQL 135
TYPE DESCRIPTION
WA R N I N G The LONG type was the first attempt to allow storage of large
amounts of data in a table, but you should use the newer types described here
instead. The LONG type stores a smaller amount of data, and you can only have
one LONG per table. Oracle plans to desupport creation of the LONG data type
and the LONG RAW data types. Neither should be created, and exisitng columns
of these types should be converted.
Table 8.4 defines the various data types. At any time you suspect that you might
have to store more than 4,000 bytes worth of data, you should use one of the data types,
all variable-length, that are outlined in Table 8.4.
136 Chapter 8
Raw Types
The raw data type are used to store binary data. Unlike character data, Oracle won’t
convert raw data when moving it between the user session and the database, the latter
possibly having different character sets. Also, raw data is imported and exported with-
out conversion. The raw type is a variable-length column, like VARCHAR2, with a max-
imum size of 2,000 bytes.
Other Types
There are several other data types that are available in a default Oracle install. PL/SQL
has data types that allow you to store data sets. These types—varray and nested
tables—are discussed in the PL/SQL section of this chapter. Also, two types are used
to describe the position of data—REF, ROWID, and UROWID—.
You can also create your own types by defining object types. In addition to the built-
in types, Oracle provides several object types. The XMLType is an example of one and
is specifically covered at the end of this chapter. The following is a list of some other
Oracle-provided key object types.
XMLType. Used to store XML data
“Any” Types. Used when the data type isn’t known or when maximum flexibility
is wanted
URI Types. Used to describe URIs
Spatial Types. Used to store spatial information (e.g., geographical coordinates)
Media Types. Used to store and easily manipulate audio, video, image, and other
media data
Oracle SQL 137
Operators
Oracle operators are used in expressions to either compare two operands or evaluate
two operands and return a result. Relationship comparison operators, set comparison
operators, logical operators, set operators, and wildcard operators are covered later in
the chapter when the select statement is discussed. The remaining operators are the
arithmetic operators and the concatenation operators, described in Table 8.5. As you’ve
seen, the * can be used to select all fields from a table.
SQL Expressions
An expression is essentially anything that results in a single value. It can include func-
tion calls, operators, literal values, columns, and even an SQL statement. Different
types of expressions are required in different parts of SQL. Table 8.6 defines the differ-
ent Oracle expressions.
(continues)
138 Chapter 8
In addition to these expressions, there are two types that are specific to PL/SQL—
case expressions and type constructor expressions. Many of these expression descrip-
tions include SQL that you have not yet encountered in this book. You’ll notice, as you
Oracle SQL 139
progress through this chapter, that some of the same examples are used. For now, it’s
important to know only that there are many types of expressions and that not all are
interchangeable—you cannot put any type of expression into all parts of SQL state-
ments that require expressions.
Describing Tables
Often, you need to determine what the columns and data types of a table are. You can
do so by using the describe statement, which works as follows:
DESC emp;
This will provide the following information about the emp table:
The describe command is very useful for finding out information about a particular
object. However, it isn’t useful for finding out how objects relate to one another, for
which you need the data dictionary. Appendix A includes pointers to online informa-
tion about the data dictionary.
Select Queries
The select statement is by far the most used statement in all of SQL. You’ve seen it a
lot already—we couldn’t have gotten even this far without using lots of examples. So
far, your use has been simple, but here in this section its full power is revealed. As you
might expect, the select statement can be quite complicated. With an understanding
of the purposes of the basic component pieces, you’ll learn the specifics of each.
You’ve seen this statement a lot so far. It returns everything in the emp table.
A couple of times, you’ve used a select statement that has a where clause, such as
the following:
This statement returns just the names of employees with salaries greater than 1,000.
This example has all the component parts. Here’s a translation of this select statement
in plain English:
SELECT the data I want FROM one or more data sources WHERE these conditions are met
The first component—”the data I want”—is called the elements clause. In our first
statement, you used the wildcard operator to say that you wanted all the fields for all
the rows, where as in the second statement you asked only for two specific fields. The
second component is the target—it tells the database what objects will be involved. The
last component, the where clause, is optional, as is demonstrated in the first statement
above. However, you’ll almost always use it in real-life code.
The first topic covered will actually be the second component—the target. You
need to know a little bit about this before you can fully understand the element
expression, which is covered secondly. The section that follows is about the where
clause.
Target Clause
The target clause is typically a comma-delimited list of tables, but it can also contain a
couple other types of objects. First you’ll learn what else can be in the list. Then, since
you have experience with select statements involving only one table, you’ll learn what
happens when there is more than one object, as well as take your first look at joins.
Finally, you’ll learn table aliasing and when it is used.
When more than one target object is listed in the target clause, the database does a
Cartesian product between the two tables. A Cartesian product is the mapping of each
member of one set with each member of another set. This is best illustrated by exam-
ple. Try the following SQL statement:
Notice that there is a lot of repeated data in the result. Also notice the number of
rows returned: 56. The emp table has 15 rows, while the dept table has 4. The number
of rows you got back is 14 multiplied by 4, or 56. If you look at the data, you’ll see that
for each employee, each department is listed. That result is a Cartesian product
between ename and dname.
You’re probably wondering, “Why would I ever want a result like that?” You really
don’t. However, what you are seeing here is the foundation operation of a relational
database, the join. You have taken two different sets of data and multiplied them so that
every permutation is available to you. You then use the where clause to choose just the
data you want. Without getting too far ahead of yourself, here is a simple example:
Now you have only 14 rows, each having the correct department name for the
employee. You’ve joined two different entities from two different tables—ename and
dname.
You can have any number of objects that you desire in your list. The Cartesian prod-
uct works the same way. For instance, if you add the bonus table to your target as
shown in the following select statement—and the bonus table has 3 rows—then you
will get 168 rows: 14 × 4 × 3 = 168. If the bonus table has 0 rows, which is the default,
you’ll get 0 rows: 14 × 4 × 0 = 0.
Target Aliasing
In the previous example, you referenced the deptno columns by specifying the table
and the column name. Aliasing allows you to declare that you want an object to be
known by a different name in the SQL statement. Here is an example:
Why would you want to do this? Three reasons. First, it can make your SQL more
readable. Second, you need to alias if you use subqueries or cursors. Third, it allows you
to perform self-joins. This is where you join a table to itself. Here is an example with the
dept table. The dept table has 4 rows, and your query will return 4 × 4 = 16 rows.
Self-joins are important for a table in which one column references another. A com-
mon use is to represent a tree structure in the table Here’s how you might use this tech-
nique to store the nodes of an XML document in a table. To simplify the example, we
have ignored attributes, processing instructions, and text nodes. Your table might look
similar to Table 8.7.
To get the names of the child and its parent, you can execute the following self-join.
It will not select the root node, since the root node has no parent listed in the table.
Subqueries as Targets
When you use an object as a target, all the data in the object is a candidate to be
returned. Oracle also gives you the ability to use queries as targets. A query is just a
select statement, most likely with a limiting where condition. Here is an example in
which you use a query as target so that you return employees with salaries greater
than 1,300.
Often, a subquery isn’t necessary. As you’ll see when you read about the where
clause, this result set can be produced more simply with a where condition. Alterna-
tively, if you do a query on this subquery a lot, you can create a view.
1 0 Root
2 1 Daughter1
3 2 GrandDaughter1
4 2 GrandDaughter2
5 1 Daughter2
Oracle SQL 143
Elements Clause
The elements clause describes what you want from your targets. It contains one or more
expressions, which are typically column names. However, they can be any valid Oracle
expression, including functions. You can alias the expressions so that the field is listed dif-
ferently in the output. Doing so is very important in terms of XSQL, so we’ll pay special
attention to it. In many cases, XSQL requires expressions to be aliased. Also covered here
is the DISTINCT keyword. It allows you to get only the unique rows in the result set.
Element Expressions
An expression, in terms of the elements clause, is any one of the following:
■■ An unambiguous column name of any object listed in the target clause
■■ A function
■■ A number or quoted string
It’s most important to note that the column name is unambiguous. In many cases,
the same column, such as deptno, will exist multiple times in the tables that you are
using. In such a case, you have to declare which column you mean. You do so by pre-
fixing the object name onto the column—emp.deptno or dept.deptno. Here is an
example that uses each of the different types of expressions:
By default, only the column names can possibly be valid XML names. You must use
aliasing for other types of expressions, which is covered in detail in the “Expression
Aliasing” section. Here is how to make our example work in XSQL:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
SELECT
ename,
‘howdy’ AS “howdy”,
1 AS “number”,
TO_CHAR(hiredate) AS “hiredate”
FROM emp
</xsql:query>
</page>
Expression Aliasing
You’ve already seen a lot of basic elements clauses. You can use the wildcard charac-
ter to fetch all the fields for each row, or you can name each row explicitly. Here are two
SQL statements that produce the same result:
As you know from the last chapter, the XML elements for each piece of data take the
name of the column. You can alter the name of the column by using aliasing. For
instance, you might want the element names Department_Number,
Department_Name, and Location in your XML. You would alias these names as fol-
lows by using the AS keyword:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql” >
<xsql:query>
SELECT
Oracle SQL 145
deptno AS Department_Number,
dname AS Department_Name,
loc AS Location
FROM dept
</xsql:query>
</page>
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql” >
<xsql:query>
SELECT
deptno AS “Department_Number”,
dname AS “Department_Name”,
loc AS “Location”
FROM dept
</xsql:query>
</page>
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql” >
<xsql:query>
SELECT ename,to_char(HIREDATE,’yyyy’) FROM emp
</xsql:query>
</page>
If you try to load this page in your browser, you will get an error. However, if you
run it in SQL*Plus, the query runs fine. The problem is that parentheses and single
quotes aren’t allowed in XML names. Thus, you have to alias the second expression to
something valid, as follows:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql” >
<xsql:query>
SELECT
ename AS “Employee Name”,
to char(HIREDATE,’yyyy’) AS “Hire Date”
FROM emp
Oracle SQL 147
</xsql:query>
</page>
Distinct Keyword
Sometimes, you want to know only what the unique members of a set are. For instance,
you might want to know only those years in which any hiring was done. With what
you know so far, you would do the following:
But then you’d have to go through all of the rows and eliminate those entries that are
repeats. Instead, you just use the DISTINCT keyword. It does this work for you.
Here’s how it works:
You can also use the UNIQUE keyword to do this—they are synonyms.
Where Clause
The real work of any select statement is done in the where clause. In fact, you couldn’t
even get through our discussions of the other two clauses without using the where
clause a couple of times! Even a fairly modest production database has thousands, if
not millions, of pieces of data. The rest of the select statement is used to get the data,
while the where clause is the tool to filter it. First, you’ll look at how the where clause
works. Next, you’ll examine joins, which are among the key components of the
RDBMS architecture, more deeply. Finally, you’ll see some complex where clauses.
SELECT ename
FROM emp
WHERE sal < 1500
ORDER BY ename
You can consider the where clause filtering as listed in Table 8.8. The six rows
selected are the six for which the expression evaluates to true. The where clause
doesn’t limit you to just one test. You can combine tests with boolean operators. Con-
sider this query in which the salary test is set differently for different departments:
(continues)
150 Chapter 8
Table 8.9
Salary Range Based on Departments (Continued)
deptno=10 and sal<1500?,
deptno=20 and sal<2000?,
ename deptno sal deptno=40 and sal<3000?
Because of the structure of the statement, only one of the conditions needs to be met
for the statement to evaluate to true. Notice that none of the employees in department
30 makes it to the result set. There is no way for them to evaluate to true. The follow-
ing statement will see employees of department 30 in the result set, because some of
them have commissions greater than 500.
Notice that Turner, who has a commission of 0, is in the result set for this query. This is to
be expected, for 0 is less than 1400. But do a quick SELECT * FROM emp and you’ll notice
that there are a lot of employees for whom no commission is specified. The value for their
commission is NULL, not 0. The value NULL has no numeric value, so it will never evaluate
to true in a range condition such as comm < 1,400. If you want to get all the employees who
have no commission or a commission less than 1,400, you can do the following:
Comparison Operators
So far, you’ve seen only five operators used—AND, OR, NOT, =, and <. The first three are
logical operators; the last two are comparison operators. As you might expect, there are
other operators available for you. Here you’ll examine all the operators that exist.
The operators you use most often are the relationship comparison operators. These
are the operators that you first learned about in math class. The key difference is that
they work against more than just numbers. You can also use them with dates and
strings. For instance, the following query,
will return all the rows that are alphanumerically greater than ‘MARTIN’. The same is
true for dates. The following query will return all of the employees hired after 1982:
All the relationship comparison operators are listed in Table 8.10. They are listed by
what they do, because several of them do the same thing. In those cases, the operator
that you’ll see used in this book is listed first.
If either operand is null, the expression will always evaluate to NULL. Here are
some examples to prove this point. They also backup our claim that NULL is not 0.
Neither query returns rows.
Table 8.11 lists the two operators that allow you to compare values to NULL.
String-Matching Operators
It is possible to do wildcard matching in SQL. The like and not-like operators provide
this functionality. Wildcard matching differs from the text searching made possible
with Oracle Text, which you’ll learn about later. While not as powerful, the like and
not-like operators work against any column and don’t carry the overhead of Oracle
Text. Both operators work the same way. Consider the following queries. The first
returns employees who have an A in their name; the second returns employees who
don’t have an A in their name.
SELECT ename FROM emp WHERE ename LIKE ‘%A%’
SELECT ename FROM emp WHERE ename NOT LIKE ‘%A%’
In the following queries, there are two wildcard characters: the % (matching zero or
more characters) and the _ (matching one character). The first query returns employ-
ees who have the letter A as the second character in their names; the second query
returns employees who have E as the second-to-last character; and the third query
returns employees who have N as the third-to-last character:
SELECT ename FROM emp WHERE ename LIKE ‘_A%’
SELECT ename FROM emp WHERE ename LIKE ‘%E_’
SELECT ename FROM emp WHERE ename LIKE ‘%N__’
If there is no wildcard character at the beginning, the string matches only those
characters that start with the string you specify. The same is true at the end of the
string. Although there are many employees who have A’s and S’s in their names, the
following statement matches only Adams:
Logical Operators
There are three types of logical operators: AND, OR, and NOT. You’ve seen AND and OR
in action. NOT is fairly intuitive when NULL isn’t involved. Here is an example that lists
everyone not in department 10. The parentheses aren’t required, but as always they’re
a nice touch. Frankly, it’s hard to overuse parentheses.
The use of NOT in conjunction with NULL values is a bit nonintuitive. It’s important
to remember that NOT always works in conjunction with a relationship comparison
operator. If an operand in the subordinate expression is NULL, the expression will
evaluate to NULL. The important thing to realize is that the inverse of NULL is NULL.
(This is different from true and false, which are inverses of each other.) So, when you
apply NOT to a NULL condition, it will still be NULL. Here is an example:
Although there are many people without commissions, they aren’t listed in the
results. For those people, the query evaluates as “select * from emp where NOT
(NULL)”. On the other hand, the query for those people who have commissions that
aren’t equal to 500 evaluates as “select from emp where NOT (false)”. In the latter case,
the where clause evaluates to true because false is the inverse of true, whereas the for-
mer statement evaluates to NULL, which is not true. If you want to get all the people
who don’t have commissions of 500, you would do it as follows:
The AND and OR operators have their own ways of dealing with NULL for the same
reasons. If both operands are NULL, then the expression is NULL. Applying NOT to it
won’t make it true. Cases where only one operand is NULL are more interesting. Con-
sider the following two examples:
In the database, Ward has a commission of 500, while Jones has a salary of 2,975. The
first query returns both Ward and Jones, while the second query returns no rows.
This is completely expected behavior. Now, this is what happens when you apply NOT:
This returns only three rows—those who have commissions that aren’t 500 and that
don’t have salaries that are 2,975. You know that commission either has a value or is
NULL and that everyone has a salary. This means that the first operand of OR can evalu-
ate to either true or false, while the second operand can evaluate to true, false, or NULL.
From this, you can deduce how the OR operator behaves when it is confronted with
an operand that has a NULL value. Since employees without commissions didn’t show
up in our result here, you know the following:
■■ If one operand is NULL and the other TRUE, an OR expression will evaluate to
TRUE.
■■ If one operand is NULL and the other FALSE, an OR expression will evaluate to
NULL.
The second observation is the nonintuitive one. It seems that when you imply a NOT,
you should get the inverse set—all the rows the query didn’t return without NOT. This isn’t
the case, however. Instead, you only got the other employees who have commissions. For
those employees, the original query evaluated to false; now it evaluates to true. For the
other employees—those without commissions—the original query evaluated to NULL.
Since the inverse of NULL is NULL, those rows don’t show up when you apply NOT.
Here’s the second case:
The original query returned no rows, so now you can expect to get all rows. But
someone is missing—Jones! Jones is our employee with a salary of 2,975 and no com-
mission. From this, you can deduce the following about AND statements:
Oracle SQL 155
■■ When one operand is NULL and one is true, the expression will evaluate to
NULL.
■■ When one operand is NULL and one false, the expression will evaluate to false.
This behavior may seem incredibly counterintuitive. The best way to think about it by
considering the overall structure of the language. Consider the following two statements:
As you’ve already learned, this statement should only display employees with com-
missions that aren’t 500. If the preceding rules weren’t in place as they exist, these last
two statements wouldn’t return the same result. Logically, they should. Putting a
truism in a statement—or not having a truism—shouldn’t affect the end result.
By now, you’re probably thinking, “Isn’t there an easier way?” The treatment of
NULLs has been debated ever since SQL started. The truth is that you have to distin-
guish between NULL values and conditions that are false. If you just treat as false any
condition in which a NULL is an operand, in your applications you’ll end up doing a
lot of work sorting out what is NULL and what is false. In practice, the theory here is
that you don’t generally want NULL conditions grouped in with false conditions. If the
inverse theory were used—you do want NULL conditions grouped in with false
conditions—we’d be talking about what steps are needed to keep them separate.
This discussion has been somewhat long-winded and theoretical. If your brain is
hurting a bit, Table 8.12 should help. If in doubt, just look up how the expression is
supposed to behave and remember the following rules of thumb:
■■ That NULL isn’t FALSE
■■ That the inverse of NULL isn’t TRUE
Table 8.12 lists the logical operators. The first and second operands are always inter-
changeable for AND and OR, while NOT only has one operand.
(continues)
156 Chapter 8
Joins Examined
In the discussion on targets, you learned that when you list more than one object in the
table you get the Cartesian product of the objects. This is almost never what you want.
Instead, you would want to use the where clause to limit the rows that are returned.
Here’s a statement that gives you the Cartesian product. Consider the following
statement:
SELECT emp.ename,emp.deptno,dept.deptno,dept.dname
FROM emp,dept
WHERE ename=’ADAMS’
SELECT emp.ename,emp.deptno,dept.deptno,dept.dname
FROM emp,dept
WHERE ename=’ADAMS’ AND emp.deptno=dept.deptno
ADAMS 20 10 ACCOUNTING
ADAMS 20 20 RESEARCH
ADAMS 20 30 SALES
ADAMS 20 40 OPERATIONS
Oracle SQL 157
Now you will get only one row. It tells you to which department Adams belongs, as
listed in Table 8.14.
In practice, you’d probably never include both emp.deptno and dept.deptno.
This example, however, shows you how joins work. When you say ename=’ADAMS’,
it will filter on all the rows of the Cartesian product of emp and dept. The join
expression—emp.deptno=dept.deptno—is simply doing the exact same thing.
The assumption is that between the tables and signified by the deptno columns is a
foreign key relationship. Figure 8.5 illustrates this relationship for the emp.deptno and
dept.deptno tables.
More than one join can exist in one SELECT statement. The same principles apply
when there are multiple joins. When there are multiple targets, the database combines
them to make a Cartesian product. At that point, the combination looks like a table,
albeit one with a lot of repeated data. You use any number of join conditions to reduce
the Cartesian product to the rows that you want.
ADAMS 20 20 RESEARCH
SELECT ename,emp.deptno
FROM emp,dept
WHERE emp.deptno=dept.deptno(+)
emp dept
deptno deptno
The (+) signifies that you want the preceding join to evaluate as true if the first col-
umn is null.
Order By Clause
When you use the order by clause, you tell the database that you want your data
ordered in a certain way. This clause tends to be very important with XSQL. Since you
can translate your result XML directly into the HTML that you present, you can easily
get your data sorted correctly just by using a where clause in your SQL. The order
by clause works very simply. Here’s an example:
SELECT job,ename
FROM emp
ORDER BY job
An ascending sort results, as shown in Figure 8.6. You can also perform a descend-
ing sort by using the desc keyword:
SELECT job,ename
FROM emp ORDER BY job DESC
Secondary sorts are easy to perform too; just separate your sorts with commas.
Here’s an example in which the jobs are sorted first, with the secondary sort on name.
You can have as many sorts as you like. It’s a good idea to have a sort clause for each
element expression. The ASC in the example specifies an ascending sort. It is the
default, and although it isn’t strictly needed, it does make your SQL statement clearer.
SELECT job,ename
FROM emp
ORDER BY job DESC, ename ASC
One final note: In the preceding examples, our sort keys have also been fields you
wish to return. This condition doesn’t have to be the case, however. In the following
statement, you sort by salary even though the salary isn’t in the output:
SELECT job,ename
FROM emp
ORDER BY sal DESC
NULL values are sorted before other values for descending sorts and after other values
for ascending sorts.
SELECT ename
FROM emp
UNION
SELECT dname
FROM dept
For set operators to work, the same number and type of fields must be in each select
statement. Table 8.16 lists all of the set operators.
160 Chapter 8
OPERATOR DESCRIPTION
MINUS Eliminates rows that appear in the second query from the
rows returned in the first query.
INTERSECT Returns only the rows that are common to both queries.
Oracle SQL 161
Of course, to do this from XSQL requires that you perform the following:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
SELECT sysdate AS “Date” FROM dual
</xsql:query>
</page>
You can also use the dual table to include parameters in the result set:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
select ‘{@param}’ AS “ParamName” FROM dual
</xsql:query>
</page>
Managing Tables
The select statement is the tool used for getting data out of the database. The flip
side of select is getting data in to the database. Before you can do that, however, you
must have places to put the data. The construction of the database is the job of Data
Definition Language (DDL) statements, which create, modify, or delete objects in the
Oracle database. This section covers the part of DDL that pertains to the most popular
and useful type of object, tables.
There are a lot of things to consider when managing your table. At the highest level,
you must decide what data you want in the table. You think about this based on how
you want this table to fit in to the rest of your application. But there are lots of system
level attributes to consider, also. Ultimately, you’ll want to work with your DBA on a
lot of these parameters. Here, you’ll get a gentle introduction.
Creating Tables
There are many options in creating tables. In this section, first you’ll examine the sim-
plest way to create a table; then you’ll examine some of the more useful aspects of table
creation. A lot of the options presented here concern how the table is stored. Finally,
you’ll learn how to create an exact copy of another table. The following SQL will create
a table for customer orders. You’ll use this table in later examples, so you should create
it under the momnpop id.
address2 VARCHAR2(50),
state VARCHAR(5),
zip VARCHAR(10),
country VARCHAR(5)
);
If you now do a desc customer, you’ll see that your table is in place. There are,
however, a few problems that you will run into immediately. First, the custid will be
used to identify your customers uniquely. You don’t want someone using the same cus-
tomer identification (id) twice. There is an easy way to prevent this from happening—
just issue the following command:
T I P To get rid of the old table, just issue the command DROP TABLE
customer;
The database will now automatically prevent anyone from reusing the same
custid. Your next problem concerns the country column. Because most of your cus-
tomers are in the United States, you will want to set USA as the default value. If the
value of the country column isn’t explicitly defined in an insert statement, it will be
USA. The first sentence asks that you set USA as the default value; the second sentence
(as well as first sentence following the display code below) says that if you don’t
explicitly define (set?) the default value, it will be USA. You can define the default
value with the following code:
Now, one thing you do want your user to define is his or her last name. If you don’t
define the last name, the row will be completely useless. You can require that the last
name be defined as follows:
N OT E The NOT NULL and PRIMARY KEY are examples of constraints, which
you will learn more about later in the chapter. The best time to create them is
along with the table, so a couple are introduced here. You’ll see all of them in a
little while.
Your next problem is that you want the table to live on a particular tablespace. You
expect to get a lot of customers, so you want this table to be on your new terabyte
drive. Here’s how you would do that, assuming the tablespace is named
cust_tablespace:
N OT E There are many other storage options for your table that allow you to
fine-tune how your data is stored. They require a deep understanding of the
Oracle architecture to be used properly. These options are beyond the scope of
this text and aren’t covered here.
164 Chapter 8
Let’s say that you want to base your table on data that already exists in the system.
By using the AS keyword, you can create a new table based on the following select
statement, and the table will automatically populate. Notice that you use the aliasing
you learned earlier to change the empno column name to EMPID.
The last topic for this section concerns temporary tables, used for storing data on a
per-session basis. The data you put in can be seen only by your session, at the end of
which the data goes away. Other sessions can use the same temporary table at the same
time and your session won’t see their data. Temporary tables are often used in complex
queries for which you need to grab a subset of data. They have limited usefulness in
conjunction with XSQL pages because of the short duration of an XSQL session. How-
ever, there may be some situations in which you’ll find it much easier and efficient to
process a subset from a temporary table rather than to repeatedly requery the database
or load the data set into memory. Here is how a temporary table is created:
Altering Tables
The alter table statement allows you to change aspects in a table that has already
been created. In many cases, you can make changes to a table that already has data in
it. This section looks at how the alter table statement works, what it is usually used
for, and what it can’t be used for. Before beginning the discussion, it’s important to note
that you can modify the storage characteristics of tables. These characteristics won’t be
itemized here, but most can be altered at any time. In this section, most of the empha-
sis is on working with columns, but it also covers moving a table to a new tablespace.
Some discussion of constraints is given, but the “Constraints” section provides the
greatest discussion of that topic.
The good news is that adding a basic column is easy. It’s even easy to add a column
that has a default value, as follows:
But if your table already has data in it, things get more complex. For instance, what
if you want to create a column as NOT NULL? Each row has to have a value. As shown
in the following, the syntax is simple enough:
If you run this against the emp table, which has data in it, you will get the following
error message:
The problem is that at the time you add the column, all the data is null. The NOT NULL
constraint couldn’t possibly be satisfied. You have several alternatives:
■■ Set a default value for the column, such as ALTER TABLE emp ADD
(home_zip VARCHAR2(10) NOT NULL DEFAULT ‘27607’);
■■ Remove all the data and alter the column
■■ Create the column without the NOT NULL constraint, add the data, and apply
the NOT NULL constraint afterwards.
The second option leads directly to the discussion about altering existing columns.
To make these alterations, you would use the modify keyword and a similar expres-
sion that you would use if you were creating the column from scratch.
You don’t need to include anything about the data type, since that would remain the
same. However, if you want to change the data type, you can. But there are restrictions.
Anything goes if the column has no data in it. If you want, you can even change the data
type entirely from varchar2 to date, date to number, number to varchar2, and so
on. If, however, there is any data in the column, this kind of modification isn’t allowed.
Table 8.17 lists the rules for modifying columns that already contain data.
166 Chapter 8
Our last topic for this section concerns dropping a column altogether, the ultimate
modification. This can be accomplished by way of the drop column clause. If there is
data in the column, it will be deleted.
Dropping Tables
Dropping tables is the process of removing a table and all of its data from your data-
base. To drop a table, you need the drop table privilege for the table. Also, you need
to be very careful; if you are having a bad day, you might end up inadvertently drop-
ping the incorrect table.
Oracle SQL 167
There are two ways to drop tables. The following way you’ve already seen:
Often when trying to drop a table, you will encounter the following error message:
You get this error message because foreign key constraints are being used. You learn
more about foreign key constraints later; for now, it’s important to know only that
there are rows in other tables that reference keys in this table. If you just go and drop
this table, those other tables will be very, very upset. Thus, Oracle doesn’t allow you to
do it. Here are ways around this problem:
■■ Getting rid of the foreign key constraints; then dropping the table
■■ Deleting the data that references this table
■■ Dropping this table and all the tables with references to it
The first two options will be examined in the foreign constraints discussion. The last
option is a bit draconian and risky: You delete the table_to_drop table and recur-
sively drop all tables that have a reference to it. Oracle will drop the tables with refer-
ences to your target table, and if it finds that it can’t drop a table because another table
has a reference to it, it will go and find that table and then drop that one too. It’s like a
scorched-earth campaign against the ORA-02449 error. Use with care.
Transactions
A transaction is one or more SQL statements executing against the database. They are
very important when you work with Data Manipulation Language (DML) statements
because you often want all of the statements to succeed or fail as a group. A classic
168 Chapter 8
Sequences
Earlier, you created a table with a primary key. The purpose of the primary key is to
uniquely identify each row in your table. Each time you insert a new row, you need to
create a new, unique key. You know that Oracle won’t let you insert a nonunique key,
but how do you generate a new key? Finding the last key used is one strategy, but what
if multiple sessions are creating new keys at the same time?
Fortunately, there is an easy solution—a sequence. An Oracle sequence generates a
sequence of numbers. The numbers are guaranteed to be unique. Each time a call is
made, the next number in the sequence is returned. Here is a simple sequence:
To use the sequence to generate ids, you can use the dual table as follows:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
Oracle SQL 169
If you are interested in getting the last value that was selected—known as the current
value—you can do that as follows:
Each time you reload the page, you will get a new sequence number. By default, a
sequence starts at 1, increments by 1, has no max value (other than 10^27), will never
cycle back to where it started, will cache 20 numbers at a time, and does not guarantee
that numbers will be generated in the order of the request. All these options can be
tweaked, as listed in Table 8.18.
Often, developers make the assumption that if they always use the sequence there
will be no holes in their list of ids. For instance, if you always use the a sequence that
increments by 1 for your order table and you have 1,000 orders, the numbers 1 to 1,000
will be your keys. There are several reasons why this may not be the case. First, if the
database is shutdown any unused ids left in the cache are lost. Second, if a transaction
has to be rolled back before completion and a call to your sequence is made, that par-
ticular id will be lost. Thus, you can be certain that all your ids are unique, but you
can’t be certain that they are all sequential.
Sequences can be altered with the ALTER SEQUENCE statement and the keywords
documented above. Sequences can be dropped using the DROP SEQUENCE statement:
Insert Statements
The insert statement is one of the simplest statements in all of SQL. You specify the
table, the columns you wish to insert, and the data. Here is an example where you
insert a new customer order from XSQL:
<?xml version=”1.0”?>
<page connection=”momnpop” xmlns:xsql=”urn:oracle-xsql”>
<xsql:dml>
BEGIN
INSERT INTO customer (custid,lname,email)
VALUES (my_seq.nextval,’SMITH’,’smith@ibiblio.org’);
commit;
END;
</xsql:dml>
</page>
You specify the columns that you wish to insert, then the values. The one additional
feature to cover for now is the use of subqueries. Imagine that you wish to insert all the
data generated by a particular select statement. You could select the data, store it
somewhere, and insert each row one at a time. Luckily, SQL makes it far easier than
that. You can embed the select statement directly into your insert statement.
Here’s how:
The only other SQL options beyond this involve partitions and subpartitions. You
can explicitly specify the partition or subpartition of your table into which you wish to
insert the data. Except for a couple of PL/SQL specific options that you will see later in
the book, that is it.
Oracle SQL 171
N OT E In your travels, you may have seen an Insert statement without the
columns specified. This only works when you are inserting a value into every
column of the table. In general, this is a bad idea. It is hard to tell which values
match up with which columns from just looking at the SQL, and if columns are
ever added or dropped, your SQL statement won’t work any more.
If there are constraints on your table, it’s possible to write an insert statement
that will generate an error. Our customer table, for instance, requires a unique, nonnull
key for custid. If you don’t insert a custid value, you’ll get an error message like
this one:
Generally, you should be able to structure your insert statements to avoid errors.
However, errors are always a possibility, if for no other reason than the database being
full. In Chapter 14, you’ll learn how to build your applications around the need to han-
dle errors like this.
Update Statements
Update statements are a little trickier than insert statements. As with insert state-
ments, you can use a subquery to specify the value you wish to insert. More important,
you almost always use a where clause to specify the rows you wish to update.
The following update statement will win you a lot of fans, because it gives all the
employees a 10 percent raise:
If you want only to give the clerks a raise, you can do so as follows:
If you want to set the value of the column based on an SQL statement, you can do so
as follows. (This gives everyone in emp4 the same salary as Adams in emp.)
Often, an update statement follows a select statement on the same table. You base
your update on the data retrieved from the select statement. But what if someone else
changes the data in the table between the time that you select it and the time that you
update it? To remedy this problem, you can append FOR UPDATE to your select state-
ments, thereby locking the rows in the table returned by the select statement.
172 Chapter 8
The real danger in the delete statement is getting the where clause wrong or acci-
dentally forgetting it. The following delete statement gets rid of all the rows in your
table:
Oracle also gives you the truncate statement to get rid of all the data in your table.
The truncate statement deletes all the data quickly—there is no way to roll back a
truncate. Only use when you know that you want all the data gone and when time
is of the essence:
TRUNCATE emp;
The following version automatically deallocates all storage associated with the
table:
Views
In the “Select Statement” section, you joined the emp and the dept tables many times.
If you find yourself using the same join over and over again, you might want to create
a view. A view is defined by an SQL statement and acts just like a table. When you issue
a select statement on the view, it will essentially translate the view by using the SQL
for which the view was defined and then return the results. When you insert or update
the view, it will insert or update the underlying tables.
SELECT ename,dname
FROM emp,dept
WHERE emp.deptno=dept.deptno;
This view has two columns, ename and dname. The options for altering a view are
exactly the same as those for creating one. Usually, views are created as follows. If you
want to modify a view, you have to specify CREATE OR REPLACE; REPLACE isn’t a key-
word by itself.
There are other keywords, detailed in Table 8.19, that you can use when creating
views. All are pretty straightforward, with the possible exception of WITH CHECK
OPTION. Examples are provided in the “Using Views” section.
If the underlying tables change, you should recompile your view. You do that as
follows:
Using Views
The whole point of views is that they resemble tables, so using them isn’t hard at all.
Here’s how you select data from the emp_dept_name view:
Inserts also work the same, but with a caveat: If the underlying tables have con-
straints that can’t be satisfied by the view, then the insert will fail. The
emp_dept_name view is an example of this. The emp and the dept tables have pri-
mary key and not null constraints. The pertinent columns aren’t part of this view, so
any attempt to insert into this view will fail.
Updates have a somewhat similar problem with views. For example, define a view
as follows:
You won’t get any updates with a query like the following, because there are no
rows with a sal of less than 1,000 in the database:
However, you are able to remove rows from the view by making changes so that the
rows won’t be selected by the view’s select statement. After running this statement,
you won’t have any rows in your view.
You may want to prevent such modifications. To do so, use the WITH CHECK
OPTION keyword as follows:
Constraints
At this point, you are ready to be a pretty productive SQL coder. You have learned how
to read data out of the database, how to administer your objects, and how to manage
the data in your objects. With just this knowledge, you could do lots of fine work with
SQL. But before writing tons of SQL, it’s important that you understand and use con-
straints. Their purpose is to keep you from shooting yourself in the foot. When you
define constraints, you restrict the kind of data that can go into your database columns.
This restriction makes it much easier for you to write applications, because it enables
you to make assumptions about the data.
Types of Constraints
With any database, there are implicit expectations of the data. For instance, you expect
that each employee has a unique id and that each belongs to a known department. Con-
straints give you a way of ensuring that the data is correct when it goes into the data-
base. This keeps you and others from having to do a lot of error handling when the data
is fetched. Table 8.20 lists the constraints in order from simplest to the most complex.
Multiple constraints are allowed, though it is redundant to define a not null or
unique constraint where a primary key constraint has already been defined. If multiple
check constraints are used, they must all evaluate to true for the data to be entered. If
two check constraints contradict each other, no data will ever be input into your table.
CONSTRAINT DESCRIPTION
PRIMARY KEY A column (or columns) must be both unique and nonnull.
Only one primary key is allowed per table.
PL/SQL triggers PL/SQL triggers are subprograms that give you procedural
control over the values that are to be input into a table.
176 Chapter 8
The unique and primary key constraints allow you to define uniqueness based on
more than one column. For instance, you can store the area code in one column and the
local phone number in another. You can then use a composite unique constraint to
ensure that the same phone number isn’t entered twice while still able to easily query
based on the area code.
A foreign key constraint is used to enforce referential integrity. Referential integrity
means that the relationship of data between tables has integrity. Generally, referential
integrity is expected to exist between columns used frequently in joins. A foreign key
constraint is enforced between the dept and emp tables that you have seen in the
examples. You can’t enter just any deptno into the emp table; it must be a deptno that
appears in the dept table. Also, you can’t delete a row in the dept table if the deptno
is in use by rows in the emp table.
In referential integrity terms, the dept table is the parent table and the emp table is
the child table. A foreign key constraint is defined on a column of the child table (e.g.,
emp.deptno) and points to a column of the parent table (e.g., dept.deptno). The
column on the child table is called the foreign key; the column on the parent table, the
referenced key. Table 8.21 lists the foreign key restraints and shows how actions are
constrained once a foreign key constraint is in place. Compound foreign key con-
straints are allowed where both the foreign key and the referenced key are composites
of several columns.
Inserts into child table Only if the foreign key matches the referenced key
in the parent table or if the values for one or more
of the foreign key composite parts are null.
Updates on parent table Not allowed if referenced key in the parent table is
changed and the original value exists in the foreign
key of the child table.
Updates on child table Allowed only if the value in the referencing columns
remains the same, is changed to a value that also
exists in the referenced key, or is changed to null.
Deletes on the parent table Allowed only if the value in the referenced key
doesn't exist in any referencing key and an on
delete clause isn't specified for the foreign key
constraint. To force deletion of the parent row and
all referencing children rows, you can use the
cascade constraints option of delete.
Oracle SQL 177
As you can see from Table 8.21, nulls are allowed on either side of a foreign key con-
straint. Generally, though, a NOT NULL constraint is applied on the columns com-
prising both the foreign keys and the referenced keys. Also, the referenced key usually
has a unique constraint, and it is typical for the primary key for a table to have one or
more foreign key constraints referencing it.
Using Constraints
Although constraints are used by the database during DML operations, as a user you
don’t see them except when they occur in error messages. You implement constraints
at the time you create tables or, possibly, by altering an existing table. It’s better to
implement constraints at the time that you create a table, because you can’t apply a
constraint to a table with data that already violates it.
You can create constraints in two ways: with names and without names. Creating
constraints without names is a little simpler, but creating constraints with names can
make the constraints easier to manage. The following example creates one of each type
of constraint:
To specify more than one constraint on a column, just enter more than one declara-
tion. You don’t have to use commas to separate the constraints. If you are going to cre-
ate composite constraints, this type of declaration won’t work. Instead, you would
need to specify the constraint by using the constraint clause given in this example to
create a composite primary key constraint. To name a constraint, you follow a column
name with the keyword constraint, the name, and the constraint definition.
As described earlier, foreign key constraints can affect the ability to delete rows in
the parent table. You can use the on delete clause of the foreign key constraint to
specify what should happen in the child table upon the occurrence of deletes in the
parent table. Here is an example:
In this case, a deletion of the parent customer will cause the custid to be set to null.
Your other option for the on delete clause is cascade, which if a customer is deleted
will cause all of that customer’s phone numbers to be deleted in the phone_bank
table.
If you need to remove a constraint, you would use the drop constraint statement:
If you created a named constraint, this should be pretty easy. If you didn’t, you’ll need
to use the data dictionary to figure out the name that was created for the constraint. For
primary and unique constraints, you can remove a constraint either with the word
primary or based on the constraint’s definition.
Date Formatting
Oracle stores dates in an internal format that isn’t very useful to you. By default, dates
are converted to a character representation that uses the default date format on the
application that makes the SQL call. For example, the default date format for XSQL is
different from the default date format for SQL*PLUS. However, if you explicitly
specify a date format, you should make it be the same regardless of the client that is
executing the SQL.
This section shows how date formatting works with SQL. Before it explores the
specifics of date formatting, it discusses the differences between SQL*PLUS and XSQL.
18.May-02
The following XSQL, which uses the same SQL query, returns a different result.
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
SELECT sysdate AS “Date” FROM dual
</xsql:query>
</page>
In this statement, the date data type is translated to a string explicitly using a date-
format mask. Since the SQL client is given a string, instead of a date, it presents the
string exactly as you described.
SQL RESULT
N OT E You can change the default date format by editing the init.ora file or
by using the ALTER SESSION statement to change it for a particular SQL session.
Altering the session is generally impractical for XSQL and can’t be done from
the xsql:query action. If you change the date format in the init.ora file, then
the default date format will be changed for everyone on your database. This
may be ill-advised if your database has other applications.
Date-Format Elements
An SQL date format mask is made up of elements that are replaced by the appropriate
value from the date in the output. Table 8.22 lists some examples of date formats. Table
8.23 lists all the elements that you can use in date formats.
ELEMENT DESCRIPTION
CC Century number.
ELEMENT DESCRIPTION
FF Fractional seconds.
I One-digit year.
IY Two-digit year.
J Julian day or days since the last day of the year 4713
B.C. Day is January 1, 4712 B.C.
MI Minute.
RRRR Last four digits of the year, when used with TO_CHAR.
SS TSecond.
(continues)
182 Chapter 8
ELEMENT DESCRIPTION
Y One-digit year.
YY Two-digit year.
SQL Functions
Oracle has a vast array of built-in functions that you can use in your SQL statements.
You’ve already seen examples of some of the functions, such as to_date and
to_char. Here, you’ll see all the functions broken down by type. Covered first are
aggregate functions. These functions, such as max, take a data set and aggregate it in to
one value. The numeric functions allow you to do math in your SQL; the character
functions, to manipulate and get information on strings; the date functions, to provide
operations on dates. There is also a set of conversion functions, as well as—
inevitably—the miscellaneous functions.
These functions are Oracle expressions and can be used anywhere an Oracle expres-
sion can be used. For instance, you can use them in the elements clause or the where
clause of a select statement or in the values clauses of insert and update statements.
GROUP BY Clauses
Group by clauses make your aggregate functions more valuable. With a single state-
ment, you can get separate aggregates across your data. To get a breakdown of job-
based salary, you would use the GROUP BY function as follows:
JOB max_sal
ANALYST 3000
CLERK 1300
MANAGER 2975
PRESIDENT 5000
SALESMAN 1600
184 Chapter 8
The GROUP BY clause can be used in conjunction with the HAVING clause to restrict
the end output. It works a lot like the where clause, except that it works to exclude the
rows based on the value of the aggregate. Here’s an example in which you get only the
job and department name pairs where the max_sal is greater than 2,500:
SELECT job,dname,max(sal)
FROM emp,dept
WHERE emp.deptno=dept.deptno
GROUP BY job,dname
HAVING max(sal)>2500
Distinct_job JOB
5 14
Oracle SQL 185
Avg
The AVG function averages all the values in its domain of rows. If a GROUP BY clause
is used, the function will be performed once for each value or unique sets of values
specified by the GROUP BY clause. AVG works on numeric values only.
The following are some examples of the AVG function.
avg_sal avg_distinct_sal
2073.21429 2064.58333
JOB AVG_SAL
ANALYST 3000
CLERK 1037.5
MANAGER 2758.33333
PRESIDENT 5000
SALESMAN 1400
UPDATE dummy_emp
SET sal=(SELECT avg(sal)
FROM emp)
WHERE dummy_emp.ename=’ADAMS’;
186 Chapter 8
Count
The count function counts all the specified values in its domain of rows. If a GROUP
BY clause is used, the function will be performed once for each value or unique sets of
values specified by the GROUP BY clause. Count works on any data type.
count_ename
14
DEPTNO count_job
10 3
20 3
30 3
Max
The max function averages all the values in its domain of rows. If a GROUP BY clause
is used, the function will be performed once for each value or unique sets of values
specified by the GROUP BY clause. Max works on numeric values only.
max_sal
5000
JOB AVG_SAL
ANALYST 3000
CLERK 1300
MANAGER 2975
PRESIDENT 5000
SALESMAN 1600
UPDATE dummy_emp
SET sal=(SELECT max(sal)
FROM emp)
WHERE dummy_emp.ename=’ADAMS’;
Min
The min function averages all the values in its domain of rows. If a GROUP BY clause
is used, the function will be performed once for each value or unique sets of values
specified by the GROUP BY clause. Min works on numeric values only.
min_sal
800
JOB AVG_SAL
ANALYST 3000
CLERK 800
MANAGER 2450
PRESIDENT 5000
SALESMAN 1250
UPDATE dummy_emp
SET sal=(SELECT min(sal)
FROM emp)
WHERE dummy_emp.ename=’ADAMS’;
Stddev
The stddev function averages all the values in its domain of rows. If a GROUP BY
clause is used, the function will be performed once for each value or unique sets of val-
ues specified by the GROUP BY clause. Stddev works on numeric values only.
stddev_sal
1182.50322
stddev_sal
1229.95096
JOB AVG_SAL
ANALYST 0
CLERK 213.600094
MANAGER 274.241378
PRESIDENT 0
SALESMAN 177.951304
UPDATE dummy_emp
SET sal=(SELECT stddev(sal)
FROM emp)
WHERE dummy_emp.ename=’ADAMS’;
190 Chapter 8
Sum
The sum function averages all the values in its domain of rows. If a GROUP BY clause
is used, the function will be performed once for each value or unique sets of values
specified by the GROUP BY clause. Sum works on numeric values only.
sum_sal
29025
stddev_sal
24775
JOB SUM_SAL
ANALYST 6000
CLERK 4150
MANAGER 8275
Oracle SQL 191
JOB SUM_SAL
PRESIDENT 5000
SALESMAN 560
UPDATE dummy_emp
SET sal=(SELECT sum(sal)
FROM emp)
WHERE dummy_emp.ename=’ADAMS’;
Variance
The variance function averages all the values in its domain of rows. If a GROUP BY
clause is used, the function will be performed once for each value or unique sets of
values specified by the GROUP BY clause. Variance works on numeric values only.
var_sal
1398313.87
var_sal
1512779.36
192 Chapter 8
JOB SUM_SAL
ANALYST 0
CLERK 45625
MANAGER 75208.3333
PRESIDENT 0
SALESMAN 31666.6667
UPDATE dummy_emp
SET sal=(SELECT variance(sal)
FROM emp)
WHERE dummy_emp.ename=’ADAMS’;
Abs
Abs returns the absolute value of a number. Returns are NULL if given a null value.
positive negative
10 10
Oracle SQL 193
Bin_to_num
Bin_to_num converts a bit vector to a number.
15 9 5 1
Ceil
Ceil returns the next greatest integer or, if the number is an integer, the integer itself.
1 2 -2
Floor
Floor returns the next lower integer or, if the number is an integer, the integer itself.
1 2 -3
Greatest
The greatest function returns the greatest number in its parameter list. Numbers
and dates can be intermixed in the parameter list, but dates use the default date mask.
It’s best to convert dates to strings first with the to_char function.
GREATEST_NUM
21
21
30
40
Least
The least function returns the least number in its parameter list. Numbers and dates
can be intermixed in the parameter list, but dates use the default date mask. It’s best to
convert dates to strings first with the to_char function.
LEAST_NUM
10
20
21
21
Mod
Mod returns the modulo of two numbers—the remainder after the first number is
divided by the second.
1 2 0
Power
Power returns the first number raised to the power of the second number.
R_10_POWER_2 R_10_POWER_2_NEG
100 .01
Round
Round will round the value to the specific number of decimal places. By default, the
number is rounded to the closest integer. If a negative argument is given, the number
will be rounded to a place to the left of the decimal place.
Sign
Sign is used to determine whether the value is positive or negative. It returns 1, 0, or
-1, depending on whether the number is positive, zero, or negative.
1 0 -1
Oracle SQL 197
Sqrt
Sqrt provides the square root of a number. Negative numbers aren’t allowed:
square_root
10
Trunc
Trunc truncates a number to the specified number of decimal places. If the number is
negative, the truncation begins on the left side of the decimal point.
FUNCTION DESCRIPTION
SIN(x) Sine
TAN(x) Tangent
FUNCTION DESCRIPTION
Character Functions
The following text describes the various character functions, which work with strings.
Ascii
The ascii function returns the numeric value of the first character of the parameter.
The value returned is based on the character set of the database and is only an ascii
value if a 7-bit ascii value is in use.
ascii(‘Z’) AS upper_z,
ascii(‘0’) AS zero,
ascii(‘9’) AS nine
FROM DUAL
97 65 122 90 48 57
Bitand
The bitand function performs a bitwise ‘and’ between the two arguments. The oper-
ation examines the numbers in binary form and sets the 1 bit in the output only if both
numbers have a 1 bit in that particular position. Table 8.59 shows how the bitand
function computes the values seen in SQL.
1 0 16
200 Chapter 8
Chr
The chr function returns the character associated with the number in the database’s
character set. If USING NCHAR_CS is specified, it would use the database’s national
character set.
SIXTY_FIVE SIXTY_FIVE_NATIONAL
A A
Concat
The concat function is equivalent to the || operator. It merges two strings.
CONCAT_STR
MANAGER-CLARK
PRESIDENT-KING
CLERK-MILLER
Greatest
The greatest function returns the alphanumerically greatest string in its parameter
list. Numbers and dates can be intermixed in the parameter list, but dates use the
default date mask. It’s best to convert dates to strings first with the to_char function.
GREATEST_STR
MAZE2
PRESIDENT
MILLER
Initcap
The initcap function capitalizes the first letter and lowercases all other letters.
INITCAP_STR
Mixed case
Instr
The instr function, which can be translated to mean “in string,” searches the first
string for the second string and returns the position. You can specify where to start
searching in the string and which occurrence to find.
2 1 5 0
202 Chapter 8
START_1 START_2_GET_2
7 13
Instrb
The instrb function provides the same functionality of instr but returns the byte
position. This is advantageous when multibyte character sets are used. It searches the
first string for the second string and returns the position. You can specify where to start
searching in the string and which occurrence to find.
2 1 5 0
START_1 START_2_GET_2
7 13
Oracle SQL 203
Least
The least function returns the alphanumerically least string in its parameter list.
Numbers and dates can be intermixed in the parameter list, but dates use the default
date mask. It’s best to convert dates to strings first with the to_char function.
LEAST_STR
CLARK
DOH1
CLERK
Length
The length function returns the number of characters in the string.
DNAME LENGTH
ACCOUNTING 10
RESEARCH 8
SALES 5
OPERATIONS 10
204 Chapter 8
Lengthb
The lengthb function returns the number of bytes in the string. This function is
advantageous when multibyte character sets are used.
DNAME LENGTH
ACCOUNTING 10
RESEARCH 8
SALES 5
OPERATIONS 10
Lower
The lower function changes all letters to lowercase.
INITCAP_STR
mixed case
Lpad
The lpad function guarantees the length of the string by prefixing characters or trun-
cating. By default, spaces are the pad character, but this can be specified.
SELECT dname,
lpad(dname,8) AS space_pad,
lpad(dname,8,’-’) AS dash_pad
FROM dept
Ltrim
The ltrim trims characters from the left side. By default, spaces are trimmed, but the
strings to trim can be specified.
Nls_initcap
The nls_initcap function capitalizes the first letter and lowercases all other letters,
based on the national character set.
INITCAP_STR
Mixed case
206 Chapter 8
Nls_lower
The nls_lower function changes all letters to lowercase, based on the national char-
acter set.
INITCAP_STR
mixed case
Nls_upper
The nls_upper function changes all letters to lowercase.
UPPER_STR
MIXED CASE
Nls_sort
The nls_sort function returns the byte sequence used to sort a string.
Replace
The replace function replaces one substring with another in your string.
REPLACE_STR
Rpad
The rpad function guarantees the length of the string by suffixing characters or trun-
cating. By default, spaces are the pad character, but this character can be specified.
SELECT dname,
rpad(dname,8) AS space_pad,
rpad(dname,8,’-’) AS dash_pad
FROM dept
Rtrim
The rtrim function trims characters from the right side. By default, spaces are
trimmed, but the characters to trim can be specified.
Soundex
Soundex is used to derive a phonetic pronunciation of the input string. It is used to
help in cases where a string may not be spelled exactly right. The first letter is retained,
all vowels h and y are dropped, and the remaining letters are encoded.
SOUNDEX_DNAME
A253
R262
S420
O163
Substr
The substr function returns a substring of the given string. The second parameter, if
positive, is the character position where your desired substring starts. If negative, the
position is an offset from the end of the string. By default, the string ends at the end of
the string. If the third parameter is passed, it will specify the length of the string to
extract.
ES ALES ALE LE
Substrb
The substrb function returns a substring of the given string where the parameters
refer to a byte position. It differs from substr only when a multibyte character set is
used. The second parameter, if positive, is the byte position where your desired sub-
string starts. If negative, the position is an offset from the end of the string. By default,
the string ends at the end of the string. If the third parameter is passed, it will specify
the length of the string in bytes to extract.
ES ALES ALE LE
Translate
The translate function changes one set of characters to another in the given string.
FROM dept
210 Chapter 8
TRANS_STR
1CC45NT3NG
R2S21RCH
S1L2S
4P2R1T34NS
Trim
The trimfunction trims characters from the left side. By default, spaces are trimmed,
but the strings to trim can be specified.
Upper
The upper function changes all letters to lowercase.
UPPER_STR
MIXED CASE
Date Functions
Date functions allow you to manipulate dates in SQL. They are some of the most
widely used functions in SQL. With these functions you can get the current time, do
arithmetic on dates, and convert between time zones. The date formats used in this sec-
tion were covered in the “Date Format Elements” section.
Add_months
The add_months function adds months to a particular date. The date is kept unless it is
too large for the new month. In that case, the last date of the last month is used instead.
SELECT
to_date(‘01-31’,’MM-DD’) AS jan_str,
add_months(to_date(‘01-31’,’MM-DD’),1) AS feb_str,
add_months(to_date(‘01-31’,’MM-DD’),2) AS mar_str,
add_months(to_date(‘01-31’,’MM-DD’),3) AS apr_str
FROM dual
Current_timestamp
The current_timestamp function returns the current timestamp from the table with
a TIMESTAMP WITH A TIMEZONE data type.
FROM dual
212 Chapter 8
DATE_STR
2002-05-19 01:05:00.000001
Dbtimezone
The dbtimezone function returns the timezone of the database:
TZ
-5:00
Extract
The extract function extracts the specified part (year, month, day, hour, minute, second,
timezone_hour, timezone_minute, timezone_region, and timezone_abbr)
from the date.
HIREDATE YEAR
09-JUN-81 1981
17-NOV-81 1981
23-JAN-82 1982
Oracle SQL 213
Greatest
The greatest function returns the greatest number in its parameter list. Numbers
and dates can be intermixed in the parameter list, but dates use the default date mask.
It’s best to convert dates to strings first with the to_char function.
SELECT
greatest(hiredate,to_date(‘16-NOV-1981’,’DD-MON-YYYY’)) AS
greatest_date
FROM emp WHERE deptno=10;
GREATEST_DATE
16-NOV-81
17-NOV-81
23-JAN-82
Last_day
The last_day function returns the last day of the month in which the specified date
falls.
HIREDATE LAST
09-JUN-81 30-JUN-81
17-NOV-81 30-NOV-81
23-JAN-82 31-JAN-82
214 Chapter 8
Least
The greatest function returns the least number in its parameter list. Numbers and
dates can be intermixed in the parameter list, but dates use the default date mask. It’s
best to convert dates to strings first with the to_char function.
SELECT
least(hiredate,to_date(‘16-NOV-1981’,’DD-MON-YYYY’)) AS least_date
FROM emp WHERE deptno=10;
LEAST_DATE
09-JUN-81
16-NOV-81
16-NOV-81
Local_timestamp
The local_timestamp function returns the timestamp in the local timezone as a
timestamp data type. This function is sometimes preferable to current_timestamp,
because the latter returns the timestamp with the timezonedata type.
FROM dual
TIME
2002-05-19 01:34:21.000001
Months_between
The months_between function returns the number of months between two dates.
Integers are only returned if both dates have the same day of the month or if both dates
fall on the last day of their respective months. A negative number is returned if the first
argument precedes the second argument. You can use the abs function to always get a
positive number.
Oracle SQL 215
SELECT months_between(to_date(‘01-16’,’MM-DD’),to_date(‘08.16’,’MM-DD’))
AS btwn_1,
months_between(to_date(‘08.16’,’MM-DD’),to_date(‘01-16’,’MM-DD’))
AS btwn_2,
months_between(to_date(‘01-16’,’MM-DD’),to_date(‘08.31’,’MM-DD’))
AS btwn_3,
months_between(to_date(‘02-28’,’MM-DD’),to_date(‘08.31’,’MM-DD’))
AS btwn_4
FROM dual;
-7 7 -7.483871 -6
New_time
The new_time function converts a time from one timezone to another.
SELECT to_char(
new_time(to_date(‘08:00’,’HH24:MI’),’EST’,’GMT’),
‘HH24:MI’) AS new_time_str
FROM dual
NEW_TIME_STR
13:00
Next_day
The next_day function returns the date after a given date that falls on the given day
of the week. If the given date falls on the given day of the week, the date one week
hence is returned. This can be fixed by always subtracting 1 from the date.
SELECT ename,
deptno,
to_char(hiredate,’DY, MON-DD’) AS hire_date,
next_day(hiredate,’Fri’) AS next_fri_1,
next_day(hiredate-1,’Fri’) AS next_fri_2
216 Chapter 8
FROM emp
WHERE deptno=30
Numtodsinterval
The numtodsinterval function converts the parameter to an interval day to second
interval. The string argument specifies the unit and can be one of SECOND, MINUTE,
HOUR, or DAY.
SELECT hiredate,
hiredate+numtodsinterval(100,’day’) AS days,
hiredate+numtodsinterval(1000,’hour’) AS hours,
hiredate+numtodsinterval(10000,’minute’) AS minutes,
hiredate+numtodsinterval(100000,’second’) AS seconds
FROM emp
WHERE deptno=10
Numtoyminterval
The numtoyminterval function converts the parameter and interval year to month
interval. The string argument specifies the unit and can be either MONTH or YEAR.
SELECT hiredate,
hiredate+numtoyminterval(1,’year’) AS years,
hiredate+numtoyminterval(10,’month’) AS months
FROM emp
WHERE deptno=10
Round
The round function rounds a datetime value to the nearest unit specified by the for-
mat. By default, the date is rounded to the precision of the default format.
Sessiontimezone
The sessiontimezone function returns the timezone setting for the current session.
SESSIONTIMEZONE
-04:00
Sys_extract_utc
The sys_extract_utc function extracts the universal coordinated time (UTC), also
known as Greenwich mean time (GMT), from the timestamp.
UTC
19-MAY-02 06.02.58.000001 AM
Sysdate
The sysdate function returns the system date from the dual table.
FROM dual
2002-05-19 01:05:46
Oracle SQL 219
Systimestamp
The systimestamp function returns the system date and time, including fractional
seconds and timezone information.
SYSTIMESTAMP
19-MAY-02 02.07.45.000000 AM -04:00
Trunc
The trunc function works much like round, except that it truncates the date to
the next lowest value. By default, the date is truncated to the precision of the default
format.
Conversion Functions
Conversion functions allow you to convert data between data types. You’ve already
used the to_date and to_char functions extensively. Table 8.106 lists the simple con-
version functions. The to_char function is examined as it pertains to numbers. (The
to_char function, as it pertains to dates, and the to_date function are not discussed
further here. For more information, read the “Date Formats” section in this chapter.)
More complex conversion functions are also discussed.
220 Chapter 8
FUNCTION DESCRIPTION
When used with dates, the to_char function takes the same format as that described
in the “Date Formatting” section. When the to_char function is passed a number, it
must decide how to appropriately output the string. It does this by using a format
mask similar to the date format mask you learned about earlier. The most important
element is the number 9. It represents a significant digit, and the number of 9’s repre-
sents the number of significant digits that you want displayed. Table 8.107 lists the
number-format elements.
Oracle SQL 221
ELEMENT DESCRIPTION
A significant digit
A padding zero.
Usually appears on the left.
D TDecimal point.
G Group separator.
V Scaled values.
Convert
The convert function converts one character set to another. If a character doesn’t exist
in the destination character set, a replacement character appears.
SELECT CONVERT(‘Ä Ê Í Õ Ø A B C D E ‘, ‘US7ASCII’,
‘WE8ISO8859P1’) AS convert_str
FROM DUAL
222 Chapter 8
CONVERT_STR
A E I ? ? A B C D E ?
Decompose
The decompose function returns a Unicode string after decomposition.
DECOMPOSE_STR
Châteaux
Translate
The translate function translates strings to either the database character set or the
national character set.
STR
text
Unistr
The unistr function returns a string in the database Unicode character set.
STR
Miscellaneous Functions
The remaining functions don’t fit easily into the other categories. The most important
functions to the XSQL developer are decode, which allows you to write conditional
logic into your SQL functions, and nvl, which allows you to substitute a value when
NULL is encountered.
Table 8.112 lists the functions.
NAME DESCRIPTION
(continues)
224 Chapter 8
NAME DESCRIPTION
User Returns the user name of the user who logged on.
Moving On
In the previous chapters, you saw examples of some SQL in the code. This chapter
helped to fill out your understanding and showed you the ins and outs of SQL. The
next three chapters delve more deeply into the Oracle database technologies. The tech-
nologies described are by no means an inclusive representation of the entire Oracle
database; rather, they are the technologies that you’ll find most useful in conjunction
with XSQL.
CHAPTER
PL/SQL
PL/SQL stands for the procedural language extensions to SQL. It gives you a new level of
power when working with the database. Instead of being limited to the functions pro-
vided by SQL, you can write your own. You can also use all of the basics of procedural
programming: loops, conditional statements, variables, and encapsulation.
The PL/SQL code that you will be creating here is stored in the database. Unlike
code that executes at the client, server-side PL/SQL doesn’t incur the costs of network
roundtrips. Calls to PL/SQL can be integrated closely with SQL. Likewise, PL/SQL
can be used from XSQL and can be a valuable tool for you as you develop your XSQL
applications. This section outlines how to create PL/SQL code and the different
options that are available.
Hello, PL/SQL!
Your first step is to create a simple PL/SQL function and execute it from an XSQL page.
Your function will simply return the string “Hello, PL/SQL!” when called. As with all
PL/SQL code, you should create it in its own file and then load the file using
SQL*PLUS. By doing it this way, if you need to change the code later, it will be easy to
do so.
225
226 Chapter 9
T I P If you get a message that you have received compilation errors, you can
use the SHOW ERRORS command from SQL*PLUS to see what those errors are.
For this example, you’ll create a package. A package is a set of functions, parameters,
and variables. The definition looks a bit like a class, but PL/SQL isn’t an object-
oriented language. Rather, the PL/SQL package is just an encapsulation mechanism.
For this example, our package contains just one sub-routine. Your subroutines can be
stand-alone, but your code will be better organized and more reusable if you always
use packages.
Your next step is to create the body of your package. This includes the actual code of
your hello_plsql. As is befitting a first try, our code is simple. It takes the param,
assumes that it is the empno for a row in the emp table, looks up the salary, and appends
it to the string “hello pl/sql:.” If the parameter passed doesn’t match an empno
in the database, then the string “invalid param” is returned.
With this package successfully compiled, you can invoke the function just like you
invoked a lot of the SQL functions. Just select it as a pseudocolumn from the dual table.
Just pass to it an employee ID that you know is valid, such as 7900.
Of course, you aren’t limited to only using this function with the dual table. You can
use this function anywhere that functions are permitted. In this example, the function
is called as an element in the SELECT statement of the emp table.
In this case, the function is called for each row in emp where deptno is equal to ten.
The result of this statement should look like this:
Your final step is to use this example in an XSQL page. Your XML page will look like
this:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
SELECT hello_pkg.hello_plsql(empno) AS hello_plsql
FROM emp
WHERE deptno=10
</xsql:query>
</page>
Structure
The preceding example gave you a taste of PL/SQL code. Now you will step back and
look at how PL/SQL is structured. Because you created a named function inside of a
package in the first example, you have already seen most of the structural components
that PL/SQL code can have. In these pages, you’ll learn the names for the parts that
you have already learned and see what the other pieces are.
PL/SQL code is defined as blocks. The individual statements (such as control state-
ments, SQL statements, variable declarations, and variable assignments) are included
in these blocks.
Block header. This block formally names and contains another block. In our
hello_plsql function, the line beginning with FUNCTION hello_plsql and
ending in a semicolon was a block header. It contained a declaration block and
an execution block. Likewise, the package declaration is another block header
that contains all of the blocks in our sample code. Block headers are optional. If
there is a block header, the code that it contains is a named block; in the absence
of a block header, the code is an anonymous block. Named blocks are generally
preferable because they are easier to reuse.
Declaration section. The declaration section declares the variables that you want
to use. It is also optional—there are many times that you don’t need to declare
variables. Our hello_plsql function had a declaration block consisting of one
line: sal_val NUMBER(7,2);. Any variable used in the execution section
must be declared in the declaration section.
Execution section. The execution section is the meat of your code. All of the code
between the BEGIN statement and the EXCEPTION statement belongs to the exe-
cution block.
Exception section. The exception block handles errors that are encountered in the
execution block. In the case of hello_plsql, it handles the case where the
SELECT statement returns no rows.
These blocks represent the core basics of PL/SQL code. In addition to these blocks,
you can also have a package specification, which was the first statement that you exe-
cuted. It declares the package to the world and declares what is in it. Now that you
know the basic parts of PL/SQL, you can start examining each part in turn.
Declaring Variables
As described earlier, all of your variables must be declared in the declaration section.
(This differs from C++ and Java, in which you can declare variables anywhere in your
code.) There are many types of variables that can be declared. Many are simple scalar
data types that are identical to what you have seen in SQL. You’ll learn those first. You
can also have record variables that are data structures containing several different
PL/SQL 229
variables of possibly different types. As you would expect from any decent program-
ming language, PL/SQL has arrays. Perhaps most important, PL/SQL has cursors. A
cursor contains the results of a SQL SELECT statement. By using a cursor, your SQL
code can easily iterate over data in your database.
Using the CONSTANT keyword declares that the variable is a constant—the value
can’t be changed. NOT NULL declares that the variable can never be set to a NULL value.
Both of these keywords require that the initial value be set—either the assignment
operator or the DEFAULT keyword can be used. Of course, you don’t have to declare a
variable to be NOT NULL or CONSTANT if you simply want to set an initial value, and
you aren’t required to set an initial value at all. You’ve already seen the easiest way to
declare a variable in the hello_plsql example. In that code, you declared a scalar
variable of type NUMBER(7,2) with the following line:
sal_val NUMBER(7,2);
This is the simplest scalar variable declaration. You give the variable name followed
by the type. Perhaps more useful, though, is referencing an existing database column:
sal_val emp.empno%TYPE;
This declaration tells PL/SQL that you want the variable to take the type of the
empno column in the emp table. If you know that you are going to be using the variable
to set data from a specific column (as you did in our example), you should do it this
way. First, if the underlying table changes, you don’t have to modify your code. Sec-
ond, it becomes obvious to anyone reading your code that the variable is related to that
particular column.
The following lines show examples of all of the remaining permutations of declar-
ing scalar variables:
Though you should declare variables based on a database column whenever practi-
cal, there are many cases in which this isn’t done. The following tables list all of the data
types that you can use when you are explicitly declaring the types of your variables.
230 Chapter 9
First, look at the numeric data types in Table 9.1. Many equate with each other. This
is for compatibility with other vendors’ systems. You shouldn’t skip around between
equivalent data types—simply pick one that you like and stick to it. This will make
your code easier to read.
Record Declarations
A record type in PL/SQL is a complex type that allows you to store a number of dif-
ferent variables in the same bundle. The variables themselves are declared similarly to
scalar variables, but without the CONSTANT and NOT NULL keywords and without a
way to specify initial values:
variable_name type_name;
The complexity comes in declaring record types. The easiest way to declare a record
type is to base it on a database table. If you are going to use your records to store rows
of data from a database table, this is the most elegant way to do it, also. The form for
this declaration is as follows:
variable_name table_name%ROWTYPE;
Here’s how you would declare a record variable that represents a row of the emp
database:
emp_rec emp%ROWTYPE;
If you need to explicitly define your record type, you do it using the following form.
All PL/SQL types are available for use in a record type. When declaring the type, you
can use the NOT NULL keyword and specify a default value. Unlike with scalar vari-
ables, these settings will pertain to all variable instances that use the particular type.
For example, this code defines a record that contains four variables, uses the NOT
NULL keyword, and sets initial values:
You use records by referencing their parts using the . operator. The following exam-
ple of an anonymous block shows you how to do this and also includes our other
examples in this section.
SET SERVEROUTPUT ON
DECLARE
emp_var emp%ROWTYPE;
TYPE dummy_rec IS RECORD (
232 Chapter 9
num_var NUMBER(6),
char_var VARCHAR2(10),
num_var2 NUMBER(6) :=8,
char_var3 VARCHAR2(10) NOT NULL :=’howdy’
);
dummy_var dummy_rec;
BEGIN
dummy_var.num_var:=5;
SELECT * INTO emp_var FROM emp WHERE ename=’ADAMS’;
DBMS_OUTPUT.PUT_LINE(‘emp_var=’||emp_var.empno);
END;
Cursors
Cursors are perhaps the most exciting feature in all of PL/SQL. You declare a cursor
with a SELECT statement, and it holds all of the results of that SELECT statement. Cur-
sor declarations use the following syntax:
CURSOR variable_name IS
SELECT statement
[FOR UPDATE [OF column [,column...]] [NOWAIT]];
The following cursor grabs all of the employees with salaries greater than
sal_param. The sal_param variable must already have been declared before the
cursor declaration. Generally, you also want it to have a value. Thus, parameters
passed into a function are often used in a cursor definition.
CURSOR emp_cursor IS
SELECT * FROM emp
WHERE sal>sal_param;
Our discussion of cursors will pick up again in the execution block section.
Array Structures
PL/SQL has two arraylike structures: INDEX BY tables and varrays. varrays act
most like the arrays of other programming languages, whereas associative arrays are
like hash tables and the INDEX BY tables in Perl. You use a varray to access your
objects by number, while you use an INDEX BY table to access your objects by name.
You’ll learn about declaring both types in this section, starting with INDEX BY tables.
To use an INDEX BY table, you must first declare an INDEX BY table type. By spec-
ifying an index type of VARCHAR2, the INDEX BY table works much like a Perl INDEX
BY table or hash table. You can link the records you put into the INDEX BY table to a
string value.
WARNING
The VARCHAR2 option of the INDEX BY clause is only available in Oracle 9.2
and higher.
You access both varrays and INDEX BY tables with the same syntax:
emp_varray(1):=emp_var;
emp_idx(1):=emp_var;
Both have several methods in common that can be used to perform the same actions.
They are accessed using the . operator and are outlined in Table 9.2.
Table 9.3 lists the methods that only exist for varrays.
METHOD DESCRIPTION
METHOD DESCRIPTION
BEGIN
{assignment |
control_structure |
exception_section |
PL/SQL_block |
Procedure_call |
SQL statement
};
[{assignment_statement |
control_structure |
exception_section |
PL/SQL_block |
Procedure_call |
SQL statement
};
. . .]
END;
PL/SQL 235
COMMIT
LOCK TABLE
SAVEPOINT
ROLLBACK
SET CONSTRAINTS
SET ROLE
SET TRANSACTION
Table 9.4 summarizes the additional features that can be used for each statement.
However, these aren’t quite all of the key differences between standard SQL and SQL
as it’s used in a PL/SQL program. The following list enumerates these key differences:
■■ SQL statements don’t write output to the screen or other output device. If you
want to do this, use the DBMS_OUTPUT package.
■■ PL/SQL expressions (and hence, PL/SQL variables) can be used anywhere that
SQL expressions can be used. Usually, they are used in the WHERE clauses.
■■ Select statements in the execution section, and use the INTO keyword to assign
the fields to a variable.
■■ DELETE and UPDATE statements are integrated with cursors using the CUR-
RENT OF keyword. This integration allows you to specify that a DELETE or
UPDATE should occur on the current row of an open cursor. Examples of these
statements will be given in the next section, “Control Structures,” when cursors
are examined.
PL/SQL 237
Control Structures
PL/SQL has three types of control structures: (1) conditional, (2) iterative, and (3)
sequential. A conditional control structure is the typical IF-THEN-ELSE code, while
an iterative control structure is best known as a loop. The sequential control structures,
which are either a NULL statement or a GOTO, have little practical benefit to the pro-
duction PL/SQL developer. They will be mostly dismissed last. Our first discussion
here pertains to conditional control structures, and then the iterative control structures
are covered.
IF expression THEN
statement;
[statement; . . .]
[ELSIF expression THEN
statement;
[statement; . . .]
. . .
[ELSE
statement;
[statement; . . .]
]
END IF;
The conditional control structure must begin with an IF, end with an END IF, and
have at least one statement. You can have as many ELSIF blocks as you like, or none
at all. You don’t have to have an ELSE block, or you can have only one, and it must fol-
low any ELSIF blocks that you might have. Here is an example that uses all parts.
SET SERVEROUTPUT ON
DECLARE
dummy_rec emp%ROWTYPE;
BEGIN
SELECT * INTO dummy_rec FROM emp WHERE ename=’ADAMS’;
IF dummy_rec.sal>1300 THEN
DBMS_OUTPUT.PUT_LINE(‘They are rich!’);
IF dummy_rec.comm>0 THEN
DBMS_OUTPUT.PUT_LINE(‘And they get a commission!’);
END IF;
238 Chapter 9
The CASE structure is similar to the IF structure. It is more convenient when all of
your logic involves the same value. You declare the variable with which you are work-
ing once and then set up a series of blocks that execute when the variable has a partic-
ular value. Here is the syntax:
CASE variable
{WHEN value THEN
statement;
[statement; . . .]
}
[WHEN value THEN
statement;
[statement; . . .]
]
[ELSE
statement;
[statement; . . .]
]
END CASE;
The following is an example that deals with multiple cases. Notice that you are free
to mix IF statements with CASE statements.
SET SERVEROUTPUT ON
DECLARE
dummy_rec emp%ROWTYPE;
BEGIN
SELECT * INTO dummy_rec FROM emp WHERE ename=’ALLEN’;
CASE dummy_rec.deptno
WHEN 30 THEN
DBMS_OUTPUT.PUT_LINE(‘sales’);
IF dummy_rec.comm>0 THEN
DBMS_OUTPUT.PUT_LINE(‘They get a commission!’);
END IF;
WHEN 10 THEN
DBMS_OUTPUT.PUT_LINE(‘accounting’);
DBMS_OUTPUT.PUT_LINE(‘May be an auditor.’);
ELSE
PL/SQL 239
There are two remaining tales to be told concerning CASE structures. First, if you do
not specify an ELSE statement and no WHEN statement is satisfactory, PL/SQL will
raise a CASE_NOT_FOUND exception. Second, PL/SQL also supports a variation on the
CASE structure, called a searched-case statement, where you don’t specify a variable
on the CASE line and, instead, have a conditional expression with the WHEN statements.
{LOOP |
WHILE condition LOOP |
FOR counter IN [REVERSE] start..end LOOP
}
{statement | EXIT | EXIT WHEN condition ;}
[statement; . . .]
[EXIT;] . . .
[EXIT WHEN;] . . .
END LOOP;
As with all PL/SQL blocks, you have to have at least one statement. If you are using
a WHILE or FOR LOOP, you can put the EXIT condition at the top of LOOP, whereas
with a LOOP, you have to specify the EXIT condition explicitly. You can always have
more than one EXIT condition. The EXIT WHEN keyword gives you an easy way to do
this. Instead of saying “if some condition then exit,” you just say “exit when condition.”
Here are examples of each type of loop, starting with the basic LOOP. Each example
does the same thing: It prints out the numbers one through ten and then exits.
SET SERVEROUTPUT ON
DECLARE
dummy_var NUMBER(2);
BEGIN
dummy_var:=1;
LOOP
DBMS_OUTPUT.PUT_LINE(‘dummy_var: ‘||dummy_var);
EXIT WHEN dummy_var>=15;
dummy_var:=dummy_var+1;
END LOOP;
END;
240 Chapter 9
The WHILE LOOP moves the EXIT condition to the top of the loop:
SET SERVEROUTPUT ON
DECLARE
dummy_var NUMBER(2);
BEGIN
dummy_var:=1;
WHILE dummy_var<=15 LOOP
DBMS_OUTPUT.PUT_LINE(‘dummy_var: ‘||dummy_var);
dummy_var:=dummy_var+1;
END LOOP;
END;
The FOR LOOP is most convenient for this kind of loop. It handles the counting for
you. You are also guaranteed that the FOR LOOP will exit—PL/SQL won’t let you
assign to the counter while the loop is in progress.
SET SERVEROUTPUT ON
BEGIN
FOR dummy_var IN 1..15 LOOP
DBMS_OUTPUT.PUT_LINE(‘dummy_var: ‘||dummy_var);
END LOOP;
END;
statement
statement
GOTO goto_target
statement
statement
statement
<<goto_target>>
statement you want to go to
because of PL/SQL’s requirement that each block have at least one valid statement. If
you have constructed a complicated series of IF-THEN-ELSE statements, you might
find that you don’t want a particular block to do anything. However, your PL/SQL
won’t execute without a valid statement. Instead of disturbing the overall structure of
your code, you can just throw in a NULL statement. In this way, you meet both goals—
The code will run, and the problem block won’t do anything. Here’s an example:
IF a < b THEN
NULL;
END IF;
Cursors
Earlier in this chapter, in the section entitled “Declaring Variables,” you got your first
glimpse of cursors. Now you get to see them in action. A cursor allows you to pro-
grammatically move through the results of a SQL query. Here is an example:
SET SERVEROUTPUT ON
DECLARE
CURSOR emp_cursor IS
SELECT *
FROM emp;
emp_rec emp%ROWTYPE;
BEGIN
FOR emp_rec IN emp_cursor LOOP
DBMS_OUTPUT.PUT_LINE(emp_rec.ename);
END LOOP;
END;
This code will print each employee’s name. Of course, you can do anything you
want with the data that is captured in the emp_rec variable. Before going any further,
you should know the syntax of the FOR IN LOOP. Notice that you can define the cur-
sor in the LOOP with the SELECT statement. It’s generally better to define it as a vari-
able, though, so that your code is more readable.
You are also not restricted to using the FOR IN LOOP as shown here. You can open
and close the cursor explicitly and fetch the records. Here is some sample code that
does the same thing using this methodology:
SET SERVEROUTPUT ON
DECLARE
CURSOR emp_cursor IS
SELECT *
FROM emp;
emp_rec emp%ROWTYPE;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO emp_rec;
EXIT WHEN emp_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(emp_rec.ename);
END LOOP;
CLOSE emp_cursor;
END;
This introduces three new keywords: (1) OPEN, (2) CLOSE, and (3) FETCH. OPEN and
CLOSE are both simple—you just follow them with a cursor name. FETCH is only
slightly more complex. You can follow it with one or more variables instead of a record.
Notice how the EXIT condition works in this case. There is a cursor attribute, NOT-
FOUND, that evaluates as true when the cursor returns some data. There are a total of
four attributes for cursors, which are described Table 9.5.
You can use cursors with UPDATE and DELETE statements, also. By specifying CUR-
RENT OF in the where clause, the update statement will work against the last row
fetched from the specified cursor. This assumes that there is nothing else in the WHERE
clause that will cause the update to skip that particular row. The following sample
code, which works against a copy of emp called dummy_emp, shows how this works.
The same syntax works for a DELETE statement.
SET SERVEROUTPUT ON
DECLARE
CURSOR emp_cursor IS
SELECT * FROM dummy_emp FOR UPDATE OF sal;
emp_rec dummy_emp%ROWTYPE;
BEGIN
OPEN emp_cursor;
LOOP
FETCH emp_cursor INTO emp_rec;
EXIT WHEN emp_cursor%NOTFOUND;
UPDATE dummy_emp SET sal=emp_rec.sal+100 WHERE CURRENT OF
emp_cursor;
END LOOP;
CLOSE emp_cursor;
END;
PL/SQL 243
ATTRIBUTE DESCRIPTION
You can also return a cursor from a function. XSQL has special support for this with
the REF-CURSOR-FUNCTION action. The idea is that instead of returning just a single
point of data, you return an indeterminate number of rows—just like a SELECT state-
ment. What is different is that you can use all of the power of PL/SQL to determine the
data that you wish to return. Here is a simple example that allows you to control
whether to get the salaries that are greater than or less than the given value:
You can access this function directly using the XSQL REF-CURSOR-FUNCTION
action:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:ref-cursor-function>
ref_cursor_example.return_cursor(1200,’greater’)
</xsql:ref-cursor-function>
</page>
244 Chapter 9
Packages
A package is a way to group your PL/SQL types, items, procedures, and functions
together. Type definitions, such as records and cursors, can be defined once for all of
your subroutines. You can declare variables at a package level that all of the procedures
and functions can share. However, all procedures and functions share the exact same
copy of the variables—if it is changed by a subroutine, all subroutines will see the same
change (i.e., they aren’t like the instance variables of object-oriented languages).
As you saw in the hello_pkg example earlier, a package definition has two parts:
(1) a package specification and (2) a package body. If you want a subroutine, type, or
variable to be publicly available beyond your package, you must include it in the pack-
age specification. Private items that are only available to the package code should only
be defined in the package body.
The following specification extends our earlier example to include a public variable,
pub_var.
This package body includes a private variable, priv_var, and a private procedure,
priv_proc.
PROCEDURE priv_proc IS
BEGIN
priv_var:=priv_var+1;
END;
Because package level variables are shared by all of the executing subroutines, their
usefulness is limited. However, it is quite useful to define types, such as records and
PL/SQL 245
cursors, once for a set of different subroutines and to group related subroutines
together. Though you can create functions and procedures as separate entities, it is
advisable to consider creating a package instead.
The declaration section is implicit—it’s between the IS and the BEGIN keywords.
The BEGIN and END keywords contain the execution block, which you learned about
previously, and can optionally have an EXCEPTION block. To fill out a basic under-
standing of PL/SQL subprograms, you need to understand how parameters are
passed to PL/SQL subprograms. Parameters can be passed as IN, OUT, IN OUT, and
NO COPY. These options are outlined in Table 9.6. Among other things, they affect
whether a parameter is passed by reference or by value. When passing by value, a copy
is made. Changes to the parameter won’t be seen outside the subprogram. When pass-
ing by reference, a reference to the actual parameter is passed. If you modify the
parameter inside your subprogram, it will be modified in the code that called your
subprogram.
246 Chapter 9
OPTION DESCRIPTION
You can’t call procedures and functions with OUT parameters with XSQL directly
when there is no place to which you can return the variable. For instance, you can call
such subprograms from inside an xsql:dml anonymous block, but not as a single
procedure or function call in xsql:dummy or xsql:include-owa. If you have a pro-
cedure or function that you need to use that has OUT parameters, the workaround is to
write a wrapper function or procedure. If you need data that is returned via an OUT
parameter, you can write a function that returns the value, or use xsql:include
-owa to write it back to the page. If you don’t need the data returned by the OUT
parameter, you can simply disregard it in your wrapper function. In general, you’ll
want to write wrapper functions, unless you are using xsql:include-owa.
By default, parameters are passed as IN parameters. The following example shows
how to specify each type:
There is one point left to be covered regarding parameters and PL/SQL. You will
occasionally see a subprogram called as follows:
update_emp(emp_number=>7790,sal=>5000,name=>’EDWARDS’);
update_emp(‘EDWARDS’,7790,5000);
Exceptions
PL/SQL uses exception handling to help you deal with unexpected conditions. Excep-
tions don’t bugproof your code, but they do make it easier to make your code bullet-
proof. Instead of having to use extensive logic to make sure that your code doesn’t
crash, you can use exception handlers to define what should happen if something does
go wrong. PL/SQL comes with several built-in exceptions, which are tied to Oracle
error codes. You can also define your own exceptions and raise exceptions from within
your execution block. This section covers all of these aspects of exceptions.
First, a more extensive example using exceptions is needed. So far, you’ve had
exception sections that only handle one exception. This example shows how you can
use the exception section to handle multiple exceptions.
There are several things that can go wrong. First, our SELECT statement might not
return any rows. If that happens, the NO_DATA_FOUND exception is raised and han-
dled. The opposite might occur, in which more than one row is returned. In that case,
the TOO_MANY_ROWS exception handler will execute. If anything else goes wrong, the
OTHERS handler kicks in.
248 Chapter 9
DECLARE
obsolete_exception EXCEPTION;
PRAGMA EXCEPTION_INIT (obsolete_exception,-3007);
my_exception EXCEPTION;
BEGIN
-- code
EXCEPTION
WHEN obsolete_exception THEN
DBMS_OUTPUT.PUT_LINE(‘obsolete feature used’);
WHEN my_exeception THEN
DBMS_OUTPUT.PUT_LINE(‘my_exception was raised’);
END;
BEGIN
statement
statement
RAISE my_exception;
statement
statement
EXCEPTION
WHEN my_exeception THEN
DBMS_OUTPUT.PUT_LINE(‘my_exception was raised’);
END;
Triggers
If you want to execute a procedure or function, you have to call it explicitly. For
instance, you called the hello_pkg.hello_plsql function by selecting it from the
dual table. Triggers, on the other hand, respond to events. Triggers are tied to inserts,
updates, and deletions of particular tables. The syntax for creating triggers is as follows:
PL/SQL 249
NAME DESCRIPTION
By default, a trigger executes once per statement. You determine whether it should
execute before, after, or instead of the declaration. However, insert, update, and delete
statements can result in more than one record being affected by a single statement. If
you wish to have the trigger fire for each row that is affected, you can designate this by
using the FOR EACH ROW keyword and optionally specifying a condition. Inside the
trigger, you can simultaneously have old and new data. Old data is whatever was there
before, and new data is what is on its way in. By default, you reference old data as “old”
and new data as “new.” If you wish, you can change this in the referencing clause.
Reference Cursors
Earlier, you learned that a function returns some variable of some type. What can be
very interesting is returning a cursor. Also, XSQL has special support for this with the
ref-cursor-function action. The idea is that instead of returning just a single
point of data, you return an indeterminate number of rows—just like a SELECT state-
ment. What is different is that you can use all of the power of PL/SQL to determine the
data that you wish to return. Here is a simple example that allows you to control
whether to get the salaries that are greater than or less than the given value:
You can access this function directly using the XSQL ref-cursor-function
action:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:ref-cursor-function>
ref_cursor_example.return_cursor(1200)
</xsql:ref-cursor-function>
</page>
Moving On
At this point, you know how to use both Oracle SQL and its procedural counterpart,
PL/SQL. You should have a solid understanding of how you use these technologies
with XSQL. In Chapters 10 (“Using Oracle Text”) and 11 (“Retrieving XML”), you’ll see
how to use Oracle to do specialized text searching and retrieving and managing XML.
Then, you’ll complete your base knowledge by learning XSLT.
CHAPTER
10
Oracle Text is a powerful technology that allows you to perform complex searches on
unstructured text data. It’s like the wildcard searching you learned about with the
SELECT statement, but much better. In addition to being able to doing simple wildcard
searches, you can:
■■ Rank your searches
■■ Do proximity searches, which are searches that require words to be close
together
■■ Stem your search terms to include all variations of a word (e.g., hike, hikes,
hiking, hiked)
This section will work through several of these examples. To complete these exam-
ples, you need to set up a table that has an indexed column. Oracle Text will work
against any of the following data types: VARCHAR2, CLOB, BLOB, CHAR, BFILE, and
XMLType. For the lessons here, you’ll use a simple table with a VARCHAR2 column con-
taining our small documents. Here’s what you do:
■■ CREATE TABLE docs_table (id number primary key, doc_name
VARCHAR2(10),text VARCHAR2(2000));
■■ INSERT INTO docs_table (id,doc_name,text) VALUES (1,’About
HTML’,’HTML is a presentation markup language that allows
you to make beautiful documents’);
253
254 Chapter 10
You specify the column in which you wish to search first, followed by the terms as a
string argument. You can use the operators AND, NOT, and OR, along with parentheses
in order to do Boolean searches. Here is an example:
SELECT doc_name
FROM docs_table
WHERE contains(text,’(presentation OR language) AND (HTML not
XML)’)>0;
You may have noticed that contains returns a number. The number is the score.
Oracle ranks the results, and you can structure your queries so that you only get
higher-ranking results. You can also access the score directly by using the score opera-
tor and labelling your queries. Here is an example:
Though there isn’t much difference in the scoring with our small sample data, you
can see that the allow doc scores higher. This is because the word language appears
twice in the allow doc. The scoring algorithm is too complex to detail here, but it is
structured so that a higher-scoring document contains proportionally more occur-
rences of the search term or terms than the other documents in the set. Table 10.1 lists
the results.
Using Oracle Text 255
DOC_NAME TEXT_SCORE
allow doc 9
HTML doc 4
XML doc 4
As you may have guessed, you could have gotten only the allow doc if you had
said contains(text,’language’,1)>4. You can also establish a threshold inside
of the search expression itself. Here is an example that will select only allow doc and
XML doc because of a threshold applied to the term ‘language’:
SELECT doc_name,score(1)
FROM docs_table
WHERE contains(text,’language>4 OR XML’,1)>0 ORDER BY score(1) DESC;
STEM $allow All words that share the same root are included
in the search.
SOUNDEX !effect All words that sound the same are included in
the search.
Here is an example using the stemming operator. If you perform the following
query, you get no rows returned. This is to be expected because the word allow
doesn’t occur in any of our documents.
SELECT doc_name,score(1)
FROM docs_table
WHERE contains(text,’allow’,1)>0 ORDER BY score(1) DESC
However, the words allowed and allows do occur, and they share the same root,
allow. If you perform the following query, you will get XML doc and HTML doc.
Notice that you won’t get the allow doc, even though it contains the word
allowance. Though allowance and allow start with the same letters, they don’t
share the same root.
SELECT doc_name,score(1)
FROM docs_table
WHERE contains(text,’$allow’,1)>0
ORDER BY score(1) DESC;
In this case, allow is the root. However, you don’t have to give the root word for
this query to work. For instance, the following query returns the same results because
allowed and allows have the same root as allowing.
SELECT doc_name,score(1)
FROM docs_table
WHERE contains(text,’$allowing’,1)>0
ORDER BY score(1) DESC
<title>Networking Basics</title>
<body> People didn’t notice the first fax machine. The second
infinitely increased the value of both. </body>
</article>’
);
INSERT INTO docs_table (id,doc_name,text) VALUES (4,’doc3’,’
<article>
<subject>software</subject>
<reference>Mythical Man Month, Dr. Fred Brooks</reference>
<title>Brook’’s Law</title>
<body>Putting more people on a late software project makes it
later.</body>
</article>’
);
COMMIT;
Next, you need to re-create the index so that Oracle is able to search on the XML doc-
uments. You’ll notice that this creation is different from the first time the index was cre-
ated. To make the document searchable by section, you first have to create a section
group. For XML documents, the easiest and most powerful way to do this is to create a
section group of type PATH_SECTION_GROUP. There are other ways to create section
groups that give you much more granular control over what is a valid section. You can
also make HTML documents searchable by using the HTML_SECTION_GROUP. Infor-
mation on these other types can be found in the “Oracle Text Reference” at Oracle’s
techweb Web site.
Now that you have created the index, you are ready to start searching. The follow-
ing query will return both documents:
SELECT id,doc_name
FROM docs_table
WHERE contains(text,’people WITHIN body’)>0
This search looks only at the text between the <body> and </body> tags of the doc-
uments. However, this might be too limited for you. What if your XML documents are
allowed to have body tags at multiple levels in your document? For such a case, you
can use the INPATH operator instead. This allows you to examine a particular element
in the document by its path. Here is an example:
SELECT id,doc_name
FROM docs_table
WHERE contains(text,’people INPATH (/article/body)’)>0;
258 Chapter 10
You can use its companion, HASPATH, to only return articles that have a certain path.
For instance, only one of our documents has the element reference. Here’s how you
would use HASPATH so that only those documents having a reference element will be
returned:
SELECT id,doc_name
FROM docs_table
WHERE contains(text,’HASPATH (/article/reference)’)>0;
Other Features
This section has given you a taste of what Oracle Text can do. There are many features
that aren’t covered here. Though you should be able to get pretty far with just the items
in the previous pages, it’s important to know what else is available. Here is a 50,000-
foot view of some other important aspects of Oracle Text:
Theme searching. If your language is English or French, you can search docu-
ments based on what they are about. Oracle classifies documents into themes
such as politics. You can search on politics and documents that don’t contain the
word “politics” but are about politics will be returned.
Searching on other formatted documents. In our examples, our documents were
simple text or XML. You can also insert and query more complex formatted doc-
uments (e.g., Microsoft Word).
Thesaurus capabilities. You can use a thesaurus to expand searches to include
synonyms of search terms.
Proximity searching. You can search and require that terms must be within a cer-
tain distance of each other by using the NEAR operator.
Stopword lists. A stopword list contains common words, (e.g., “this” or “that,”)
that shouldn’t be indexed. You can add your own words to the stopword list.
As you learn more about Oracle Text, hopefully you’ll get the chance to explore
some of these areas. From an XSQL perspective, nothing in this list changes how you
should write your code. Rather, these items give you a way to extend on the back end.
Moving On
This chapter focused on Oracle Text, which you can use to create robust search engines
using XSQL. You’ll see an example of how to build a search engine in Chapter 14,
“Building XSQL Web Applications.” Oracle Text works with text that is stored in the
database. In Chapter 11, “Retrieving XML,” you’ll see how you can work with XML
that is stored in the database.
CHAPTER
11
Retrieving XML
Oracle can store any media type in the database—XML is no exception. In the previous
pages, you saw how to query against XML documents stored in the database. This sec-
tion examines how to return XML—as opposed to strings—into your XSQL. The first
tool you’ll examine is the xsql:include-owa action, which allows you to write XML
into the XSQL result. Next, you’ll learn about the XMLGEN utility. It essentially allows
you to do what XSQL does—generate XML based on a SQL query. The difference is that
you can generate XML from inside a PL/SQL procedure. The last section covers a new
feature of Oracle 9i—the XMLType. This allows you to easily store and query XML doc-
uments stored in Oracle. Before beginning the lessons, it’s important to understand
what is hard about retrieving XML with XSQL. The first section covers these difficulties.
What’s So Hard?
In the previous section on Oracle Text, you saw that it is quite easy to store XML in the
table. Just insert it like any other text. The size of our test documents was limited, but
that is easily remedied—just use a CLOB type and you can store gigabytes of XML. You
might be wondering, “Can’t you just select the text?”
261
262 Chapter 11
Well, you can, but not with the xsql:query action. Consider the following XSQL:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
SELECT text
FROM docs_table
WHERE contains(text,’software INPATH (/article/body)’)>0
</xsql:query>
</page>
It will execute easily enough and return the result shown in Figure 11.1.
It looks like XML, so what’s the problem? Upon closer examination, you’ll see that
the XML returned by the query isn’t XML at all! Notice that Internet Explorer didn’t
render it with the customary colored tags. When you look at the source, you’ll see that
all of the essential XML characters have been escaped and replaced with their URL-
encoded equivalents.
What a shame! In the context of this example, it seems that you have uncovered a
horrible failing of XSQL. But it isn’t, really. XSQL’s default behavior is to assume that
XML isn’t being returned. Thus, it’s proper and required to escape the special XML
characters. As you’ll see, the workarounds are pretty easy. In this chapter, you’ll learn
about the PL/SQL-based workaround. You can also retrieve XML with the help of cus-
tom XSQL action handlers, as you’ll see in Chapter 18, “Custom Action Handlers.”
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:include-owa>
htp.italic(htf.bold(‘hello’));
</xsql:include-owa>
</page>
This will produce the output seen in Figure 11.2. The <I> tags are included in our
XML result, and the angle brackets weren’t replaced with < and >. How do the
htp and htf procedures escape escaping? The procedures in the htp package write to
the server-side page buffer.
Here’s how the htp procedures and htf functions are able to get the angle brackets
all the way through to the browser:
■■ The htf functions wrapper their argument with the appropriate tags and
return the resultant string. In this case, hello is wrappered and
<b>hello</b> is returned.
■■ The htp procedures also wrapper their argument. In this case, the wrappering
produces <i><b>hello</b></i>.
■■ Instead of returning anything, the htp procedures write their result to the
server-side page buffer.
■■ The xsql:include-owa action reads the contents of the server-side page buffer
and writes those to the output without escaping the reserved XML characters.
In all likelihood, you don’t have any interest in using the htp and htf subprograms
to generate HTML for your XSQL. (If you do have a strong interest in creating such an
architecture, consider taking a head-clearing walk. There be dragons!) From an XSQL
standpoint, there is really one subprogram of interest—htp.print.
The htp.prn procedure allows you to print an arbitrary string to the server-side
page buffer without a trailing carriage return. (If you want a trailing carriage return,
use htp.print.) You aren’t limited solely to HTML tags, and all of the angle brackets
will stay intact. The following procedure works this magic:
The procedure selects the articles that have the word “software” inside the body ele-
ment of the XML documents. Before looping through the cursor, a start tag for the root
element is printed to the buffer. This is required because the xsql:include-owa will
return an error if the XML that you write isn’t well formed. In the loop itself, each doc-
ument is written to the buffer and the loop exits at the end of the cursor. The final step
is to close the root element. The procedure can be called with the following XSQL:
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:include-owa>
Retrieving XML 265
get_doc_xml;
</xsql:include-owa>
</page>
The result is an XML document that contains two XML documents stored in the
database, as shown in Figure 11.3.
In our example, the query was hard coded. As you learned earlier, however, you can
easily make the queries completely dynamic and based on user input.
For Oracle 9i, the DBMS_QUERY package will XML-ify a query into a CLOB variable.
You then break the CLOB into strings that can be passed to the http.prn procedure.
<?xml version=”1.0”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:include-owa>
use_dbms_xmlgen;
</xsql:include-owa>
</page>
At the Oracle 8i level, the code is slightly different: It doesn’t use contexts, and the
SQL statement can be handed directly to the getXML procedure. However, the XMLGEN
package has been deprecated. Oracle 9i users should use the DBMS_XMLGEN package
instead.
In both of these cases, your result is going to look exactly the same as if you executed
the SQL using the xsql:query element. The XML generated using these APIs is also
in the canonical format.
So, for these simple examples, you’ve done a lot of work in order to do what XSQL
does anyway! In general, if you can get the same result using a straight XSQL query,
then you probably should. Of course, with this methodology you can apply a lot of
procedural logic, as you did with reference cursors a few pages ago. But then, you can
probably accomplish the same logic using the xsql:ref-cursor-function action.
It works great any time you wish to procedurally create the SQL statement whose
results you want in XML. And again, if you can get the same result using xsql:ref
-cursor-function, then you should employ that action instead.
There are some cases where you will find use for the DBMS_XMLGEN (or XMLGEN)
packages at the PL/SQL level. The key advantage is that you can assemble your XML
at a very atomic level and have great control over it. You can dynamically select exactly
the XML elements you wish to put into the output and base those decisions on any data
you can access in the database. If you need to grab a couple of rows from the database,
you can put them into the output with the htp.prn procedure discussed earlier.
However, it’s important to remember that the DBMS_XMLGEN (or XMLGEN) packages
used in this way are just one tool in your toolbox. You should probably keep it in the
bottom of your toolbox and reach for xsql:query and xsql:ref-cursor
-function. Also, you should understand action handlers before basing too much
code on this approach. An action handler can also give you fine-grained control over
the XML, as well as give you more precise ways of handling elements than the
approach described here.
XMLType
In our earlier example with Oracle Text, you stored the XML as a simple string. Oracle
9i users can also store documents using the XMLType. The XMLType gives you much
more power over your XML than you have with simple strings. There are two ways to
use XMLType: (1) You can create an XMLType object at runtime based on a string or
clob, or (2) you can create a column in the database of XMLType. Having an XMLType
as a column is more efficient, so you’ll learn about this method first. However, it’s often
impractical to do this, especially if you are working with existing database schemas.
You’ll see how to construct and use the XMLType dynamically, also. This section con-
cludes by detailing all the procedures and functions you can use in conjunction with
XMLType.
Here is how you get started. Your first step is to create a table that has an XMLType
column. You can do that as follows:
Now you want to put some data in to your table. The only difference between this
insert and the plain-text insert is the call to sys.XMLType.createXML. This changes
the string data into the XMLType.
As before, you have to create an index on your column, and you’ll have to update it
every time that you update the data. However, you don’t have to create a section group
first. Oracle already knows it’s dealing with XML.
At this point, you can do all of the types of searches that you did earlier with Oracle
Text. Here is one example:
SELECT xml_doc
FROM XMLType_table
WHERE contains(xml_doc,’people INPATH (/article/body)’)>0;
Using the xsql:include-owa action, you can also use the htp.prn technique to
push the document into the XSQL output. In this particular case, the documents are
less than 4,000 characters, so you can use the getStringVal function. However, if the
documents are larger, a runtime error would be generated. If there may be documents
longer than 4,000 characters, you would have to iterate through a CLOB, as you did in
the previous section.
SELECT a.xml_doc.getStringVal()
FROM XMLType_table a
WHERE CONTAINS(xml_doc,’people INPATH (/article/body)’)>0;
BEGIN
htp.print(‘<article-set>’);
OPEN doc_cursor;
LOOP
FETCH doc_cursor INTO xml_doc_str;
EXIT WHEN doc_cursor%NOTFOUND;
htp.prn(xml_doc_str);
END LOOP;
htp.print(‘</article-set>’);
END;
At this point, you haven’t done anything more than you did when the XML was just
plain text. The following example takes you into new territory. The extract function
allows you to pull just the nodes you want from the document. By simply altering our
cursor statement as follows:
CURSOR doc_cursor IS
SELECT a.xml_doc.extract(‘/article/subject’).getStringVal()
FROM XMLType_table a
WHERE CONTAINS(xml_doc,’people INPATH (/article/body)’)>0;
you can get a result that just returns the subject nodes. But the getStringVal is still
limited to 4,000 characters. If the node that you’re extracting (including the text, all the
children, and all of the tags) contains more than 4,000 characters of data, a runtime
error will be raised.
With this simple SELECT statement you are solving a complex problem very ele-
gantly. The relational model is great at storing structured data, such as a company’s
payroll or accounts receivable, but has a hard time with unstructured document data.
XML is great for formatting documents, but it is complex to store and search across sets
of XML documents. By storing XML documents in the Oracle database, you get all of
the benefits of the relational model plus the backup, recovery, and performance
afforded by Oracle. By using Oracle Text in conjunction with the XMLType, you can
easily search the documents and extract the nodes that you want. You’ll be seeing more
of this lucrative combination in Chapter 14, “Building XSQL Web Applications,” when
you build your first XSQL application from scratch.
In this case, you had XMLType set up at the database level. You can also instantiate
XMLType at runtime. In the following example, you return to the docs_table used in
the original examples of Oracle Text. In that table, the XML is stored simply as a
270 Chapter 11
VARCHAR2. If you want to use the procedures and functions of XMLType with that
XML, you can do so by using the following createXML function:
SELECT
sys.XMLType.createXML(text).extract(‘/article/subject’).getStringVal()
AS subject_node
FROM docs_table
WHERE CONTAINS(text,’people INPATH (/article/body)’)>0;
By replacing the SELECT statement in the cursor definition in the createXML func-
tion can take either a string or a CLOB as its argument. However, as before, the get
StringVal is still limited to 4,000 characters, and a runtime error will be generated if
your extraction is more than that.
In the preceding examples, you’ve seen three of the subprograms available to XML
Type: (1) createXML, (2) extract, and (3) getStringVal. Table 11.1 describes the
member functions that are available to you. The member functions can be called on an
xMLType object, while the static functions can be called at any time.
As with all functions, they can be compounded. This is most useful with the
extract function. You can extract a subset of the document—which could possibly
only be one node—and then perform further functions on it. This is typically how
getNumberVal and getStringVal are used.
FUNCTION DESCRIPTION
Moving On
This chapter showed you how you can access XML in your database. Now, the cover-
age of the Oracle database is complete. You’ve learned the basics of Oracle SQL,
PL/SQL, Text, and now XML retrieval and how to use these technologies with XSQL.
Now that you’ve mastered data retrieval, the next step is to learn about presentation of
your data. The next few chapters cover XSLT.
CHAPTER
12
XSLT
This chapter covers one of the most exciting XML standards, XSLT. An XSLT processor
takes an XML document and an XSLT stylesheet as input and produces whatever the
stylesheet tells it to. This can be HTML, XML, some other markup language, text, or
anything else that you can imagine. This chapter walks you through XSLT, starting
with a conceptual overview. XSLT isn’t your typical programming language, and a lot
of procedural programmers dive in a little too quickly. Hopefully, the first section will
help you avoid that fate.
The next section looks at how to create stylesheets using your current Web skills.
Generally, stylesheets are designed to template Web pages, and they include a lot of
HTML in them. However, XSLT stylesheets have to be well-formed XML documents,
and traditional HTML is not. XHTML is a standard that makes HTML XML-compliant.
Though stylesheets aren’t required to be XHTML, you can use the XHTML standard as
a way to make old HTML XML-compliant and in the creation of new Web documents.
In the next chapter, you get in to the heart of XSLT. The first step is to look at all of
the different elements of XSLT. These elements merge your source XML with the tem-
plates in your stylesheet. Next, XPath is covered, which is the language that allows you
to navigate the input XML tree. We wrap up with an overview of the XSLT and XPath
functions available to you.
273
274 Chapter 12
is created in the same way by default. Second, the real issue for developers is learning
how to use XSLT in the best way
XSLT has some of the facets of procedural programming, such as loops and condi-
tional processing. However, it is much more closely akin to HTML than it is to a proce-
dural language. If you try to use it like a procedural language, then you are likely to get
into trouble. Your code will be messy and difficult to maintain. Though a little
spaghetti code is a fact of life on development projects, it’s especially defeating in the
case of XSLT. A primary purpose of XSLT is to make it easy to separate application
development from front-end design. However, if messy code is interjected into front-
end markup, then that purpose has been subverted.
As you learn XSLT, it’s important to keep the readability and reuse goals in mind.
XSLT isn’t a particularly modular language, but with the right approach you can reuse
code. Most important, however, you should work with your teammates to ensure that
the XSLT is maintainable. In practice, this often means looking beyond the first tools
that you would use as a procedural programmer, such as iteration, variables, and con-
ditional processing.
The most powerful tool in the XSLT arsenal is XPath, the language for locating
nodes in an XML tree. You’ll find that you can accomplish a lot with this simple lan-
guage in a situation in which you would normally require a lot of loops and if state-
ments. A good rule of thumb is to try to separate the interface problems so that you use
loops as little as possible. This will force you to use templates and to make better use of
XPath. In the end, you’ll have more modular stylesheets that are more usable by the
rest of your team.
XML Element 4
XSLT
XML Stylesheet +
Output
Figure 12.1 The push model.
An XSLT stylesheet is typically a compromise between the push and the pull mod-
els. In general, the push model is simpler and more elegant. It requires less heavy lift-
ing inside the stylesheet, and thus the stylesheet is more understandable and
maintainable. However, you are at the mercy of the XML input. In the case of XSQL,
this means that you are at the mercy of the canonical XML datagram. In spite of this,
you still have a lot of control that you can exercise to keep your stylesheets clean and
elegant.
Data Template
Data Template
Data Template
Data
Data
XSLT
XML Stylesheet
+
Output
Figure 12.2 The pull model.
XSLT 277
This starts by wisely constructing your SQL queries. If the SQL in your XSQL closely
resembles how the data should appear in the output page, then you will have an eas-
ier time coding your XSLT. For instance, you can use an order by clause in the SQL
queries instead of sorting the data in the stylesheet. You can use cursor subqueries that
produce XML that is deeper than that achieved with simple queries. In some cases, you
may actually find it easier, and possibly more efficient, to do two queries that fetch the
same data in different ways. This might keep you from having to pull data out of a
result tree into a very different format.
T I P If you find that you have some problems with older browsers, you can
direct the XSLT processor to output HTML by using the <xsl:output> element
described later in this chapter. It will change non-HTML-compliant XHTML tags
to the equivalent HTML tags, resolving the XHTML problems with older
browsers.
278 Chapter 12
This section will act as a primer on XHTML. It doesn’t fully lay out the specification,
because if you already know HTML, you don’t really need to see it. Rather, the focus is
on how to make HTML documents into XHTML documents that can be used with
XSLT. The first section looks at the specific changes that you need to make to existing
HTML documents to make them XHTML compliant. Our discussion on XHTML ends
by looking at some foolproof ways to make your HTML documents XHTML compliant
and ready for stylesheet action.
XHTML Defined
A document is an XHTML document if it is well-formed XML and is valid against one
of the XHTML document type definitions (DTDs). When transitioning HTML docu-
ments to XHTML, you must make a document well-formed XML. Because certain tags
in HTML aren’t valid XML elements, part of this step is modifying these tags. The doc-
uments also must be valid, so you have to choose the appropriate DTD for your case.
As is the case with all valid documents, your XHTML document must agree with the
structure outlined by those DTDs. This section outlines each of these constraints.
First, let’s look at making your HTML well formed in accordance with the guide-
lines of XHTML. The requirements are as follows:
All tags must be properly nested. Because Web browsers tend to be very forgiv-
ing, there is a lot of HTML out there that isn’t properly nested. Fixing those
cases is really just an issue of making a document HTML compliant.
All tag names must be lowercase. HTML is case insensitive; however, XML is
case sensitive. XHTML requires all of its tags to be lowercase.
All attribute values must be quoted. HTML allows attribute values to be
unquoted as long as the value doesn’t contain spaces. XML forbids this.
Script should be encapsulated as CDATA. Though not strictly required, XML
will try to escape special characters (e.g., & and <) that often occur in scripts.
This is easily avoided by escaping the entire section as CDATA.
Minimized attributes aren’t allowed. A minimized attribute is an attribute that
doesn’t have a value. The checked attribute of the input element and the
noshade attribute of hr are common examples. These attributes must be given
some value.
Dangling open tags aren’t allowed. HTML allows dangling open tags (<p> and
<img>. XML requires all elements to be closed—a start tag must have a match-
ing end tag, or the element must have the form of an empty element.
The last requirement opens the door to some complexities. To achieve XML compli-
ance, you can either change the danglers to empty tags or give them matching end
tags. Most browsers will accept either strategy, although some will have trouble with
the latter. XHTML defines specifically which tags should be empty elements, as listed
in Table 12.1.
XSLT 279
The space after the element name is deliberate. It helps XHTML to be compatible
with older browsers. All other elements should be closed with a matching tag (e.g.,
<p> and </p>).
Minimized attributes also change in XHTML. XML doesn’t allow you to have dan-
gling attributes without a value. Table 12.2 lists the affected attributes and how they
should be translated into XHTML.
checked checked=”checked”
compact compact=”compact”
declare declare=”declare”
defer defer=”defer”
disabled disabled=”disabled”
ismap ismap=”ismap”
multiple multiple=”multiple”
noresize noresize=”noresize”
noshade noshade=”noshade”
nowrap nowrap=”nowrap”
readonly readonly=”readonly”
selected selected=”selected”
280 Chapter 12
After observing these rules, you have a well-formed document. This is all that you
need to use your HTML as an XSLT stylesheet. To go ahead and make your document
XHTML, it must validate against one of the XHTML DTDs that are listed in Table 12.3.
This involves putting a DOCTYPEdeclaration at the top of your file. When you transi-
tion your XHTML to an XSLT stylesheet, you will need to remove the DOCTYPE decla-
ration.
Generally, you are going to want to use the Transitional DTD. You declare this
by using the following at the top of your document:
If you use frames and you want your frameset page to be XHTML, you need to use
the following DOCTYPE declaration:
If you wish to use the Strict DTD, this is the declaration that you want:
The key requirement that both the Transitional and Strict DTDs enforce is
that a head section is required. Even if it is empty, you still have to have it. The strict
DTD goes a bit further on a couple of points: It is much stricter about structure and
doesn’t allow deprecated attributes.
DTD DESCRIPTION
When using the Strict DTD, you will probably run into the structural require-
ments when a child element of the body tag isn’t a block element. Only block elements
can be direct children of the body element. The following are block elements:
HEADING ELEMENTS
<h1>
<h2>
<h3>
<h4>
<h5>
<h6>
LIST ELEMENTS
<ul>
<ol>
<dl>
BLOCKTEXT ELEMENTS
<pre>
<hr>
<blockquote>
<address>
<div>
<table>
<fieldset>
<ins>
<del>
<script>
<noscript>
The other implication of this is that text can’t dangle inside the body tag. It must be
enclosed in something—usually a <p> element. The Strict DTD also forbids the use
of deprecated attributes. This is more troubling, because you lose compatibility with
older browsers because of this. The structural requirements lead to better HTML, but
using the Strict DTD works against good, open site design because it takes away
older features from you. Generally, the Transitional DTD is preferred.
282 Chapter 12
<html>
<body>
<H1>Hello!</H1>
<p>
<IMG SRC=blah.gif ID=my_picture>
<br>
Hi! This is my home page. I want to tell you a few things.
<p>
<ul>
<li>I like playing on the Internet
<li>I just want to dance!
<li>I wok in the would
</UL
That’s about all <br>
I have for now. <BR>
<p>
Maybe I’ll have <br>
more to say <Br>
later. <bR>
<Hr>
<b><i>Later!</B></I>
</boDY>
</HTML>
This HTML is quite ugly, but it will work in most all Web browsers. However, it
doesn’t stand a chance of being a valid stylesheet and is a long way from being valid
XHTML. Let’s examine the worst offense first:
<b><i>Later!</B></I>
The tags aren’t properly nested. As you learned in Chapter 3, “Hello, XSQL!”, the
tags have to be properly nested for an XML document to be well formed. The first step
is therefore to straighten that out. This yields the following:
<b><i>Later!</B></I>
However, you still aren’t out of the woods. XML, unlike HTML, is case sensitive.
These tags aren’t a well-formed fragment, because the closing tags are capitalized,
whereas the opening tags aren’t. This line needs to be fixed to be:
<b><i>Later!</b></i>
XSLT 283
Our document has this case mismatch with almost all of the opening and closing
tags. Going through and making the end tags match all of the start tags fixes most of
this problem. However, the one set of tags that already match have their own problem:
<H1>Hello!</H1>
XHTML requires that all element names be in lower case. Thus, this needs to be
changed to:
<h1>Hello!</h1>
Now, all of the start tags have matching end tags, and all of the tag names are low-
ercase. However, our document still isn’t well formed. Our document includes several
common HTML tags—<img>, <p>, <hr>, <br>, and <li>—that are illegal in XML.
They have the form of the start tag, but they don’t have a matching end tag. They
should either have an end tag or take the form of an empty tag. XHTML requires that
the <img>, <hr>, and <br> tags should be empty tags, while <p> and <li> should be
closed with </p> and </li>, respectively.
These aren’t the only tags in traditional HTML that aren’t XML compliant. Table 12.1
describes how to handle the remaining tags that either should be empty or should be
closed. But before exploring that, you are only a couple steps away from having an
XHTML document. Our next problem deals with attributes. Attributes must be quoted,
and the attribute names must be lowercase. This yields the following:
You need one other fix on the img element: The name attribute used by a, applet,
frame, iframe, img, and map is deprecated in XHTML and replaced by the id
attribute. The easiest way to fix this and to maintain compatibility is to specify the id
attribute and the name attribute. Also, the alt attribute is required in XHTML.
<img src=”blah.gif”
name=”my_picture”
id=”my_picture”
alt=”blah blah blah”/>
There is one fix left. The hr tag has a minimized attribute, “no_shade”. Such attrib-
utes aren’t allowed in XML, because attributes must have a value. The <hr> tag should
be changed to:
<hr noshade=”noshade”/>
Now you have completed the transformation of the document to XHTML. Here is
the final product. It validates against the Transitional XHTML DTD.
<head>
<title>Hello!</title>
</head>
<body>
<h1>Hello!</h1>
<p>
<img src=”blah.gif” id=”my_picture” name=”my_picture” alt=”blah
blah blah”/>
<br/>
Hi! This is my home page. I want to tell you a few things.
</p>
<ul>
<li>I like playing on the Internet</li>
<li>I just want to dance!</li>
<li>I wok in the would</li>
</ul>
<p>
That’s about all <br/>
I have for now. <br/>
</p>
<hr noshade=”noshade” />
<p>
<b><i>Later!</i></b>
</p>
</body>
</html>
If you want compliance with strict XHTML, then you simply need to remove the
deprecated attributes and add the namespace attribute to the HTML element. The fol-
lowing file is in compliance with Strict XHTML.
You can then run a document through the validator at validator.w3.org. Simply
put the file on a public Web site, navigate to validator.w3.org, and type in the URI.
You should have one of the XHTML DTDs in place to make sure it is valid XHTML.
From there, your next step is to transition it to a stylesheet. Here is the process:
1. Get rid of any DOCTYPE instructions.
2. Add xsl:version=”1.0” as an attribute/value pair to the html element.
3. Save the file as a stylesheet with the .xsl extension.
4. Create a dummy XSQL file that references the stylesheet.
286 Chapter 12
The dummy XSQL file doesn’t need to contain any actual SQL queries. In fact, it
should be as simple as possible. This is just a way to check and make sure that the
Oracle XSLT Processor likes your file. The following will do great:
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”my_stylesheet.xsl”?>
<page>
</page>
If your file is okay, the XSLT processor is just going to display the page to the
browser. After all, there is no actual XSLT code that needs to be processed. However, if
there is a problem with your stylesheet, then the translator will report an error back to
you. Figure 12.3 shows an example.
The error was generated by using the original ugly HTML example that appeared
previously. If nothing else, you can use the XSLT processor to iteratively fix your
HTML, line by line.
Moving On
This chapter gave you a high-level overview of XSLT, which should have comple-
mented the earlier examples that used XSLT. Chapter 13, “XSLT In-Depth,” covers the
details of coding XSLT. Because XSLT controls how your XSQL application looks, it’s
an important part of your overall code. You’ll see this in Chapter 14, “Building XSQL
Web Applications,” when you create your own application. A lot of the overall code is
XSLT stylesheets.
CHAPTER
13
XSLT In-Depth
This section details all the elements in XSLT based on what they do. It is meant as a
tutorial on the first reading and then a quick reference after that. It starts with the root
element and then introduces all the elements, including their syntax and examples.
The following XSQL is used for all the examples in this section. It produces the data for
the employees in the emp table. To use the examples in this section, be sure to change
href to whatever name you give the stylesheet.
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”some-stylesheet.xsl”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
SELECT * from emp
</xsql:query>
</page>
At this point in your reading, it is assumed that you know how XSLT stylesheets fit
in with the rest of the XSQL framework. In general, a datagram is returned from the
database and in the canonical format, and you use an XSLT stylesheet to transform it
into something useful to the client. With certain actions—xsql:insert-request,
xsql:update-request, xsql:delete-request, and xsql:insert-param—
you can also transform the input using an XSLT stylesheet. From there, you’ll learn
about the selection of values from the inputted XML document.
287
288 Chapter 13
This chapter looks at all the details of using XSLT stylesheets. You’ll learn about all
the XSLT elements, as well as XPath, which allows you to search locate nodes in the
inputted XML document. The first discussion concerns the root element; the next dis-
cussion shows you how to control the output with the xsl:output element. From
there, you get into the core of XSLT—templates. You can’t really write a simple exam-
ple without using templates, so you’ve seen them before. In Chapter 14, you’ll get a
chance to apply your knowledge learned here to a real-world application. Also in that
chapter, you’ll see how to iterate through sets of nodes and how to use conditional
logic. From there, you’ll learn about the rest of the XSLT elements, in addition to how
to create XML entities and handle special cases with text, how to number elements,
how to deal with variables and parameters, how to reuse stylesheets, how to sor, and
how to handle whitespace. The chapter concludes by looking at whitespace.
Root Element
A stylesheet is an XML document; thus it requires a root element. The XSLT specifica-
tion says that the root element should be either xsl:stylesheet or its synonym,
xsl:transform. In practice, the Oracle XSLT processor will accept any element as
the root element as long as the attributes required of the xsl:stylesheet element
are attributes of the root. This section looks at the syntax of the root element and pro-
vides examples.
xsl:stylesheet Syntax
The xsl:stylesheet element can only be the root element of an XSLT stylesheet. It
is completely synonymous with the xsl:transform element. Its attributes describe
the stylesheet to the XSLT processor. You can optionally use the html element as the
root and simply add these stylesheet attributes to the html element. The syntax of the
xsl:stylesheet element is below. A stylesheet element is required to be the root ele-
ment and only one stylesheet element is permitted per stylesheet.
<xsl:stylesheet
version = “version_number”
xmlns:xsl = “namespace_URI”
id = “id”
extension-element-prefixes=”extension_prefix_list”
exclude-result-prefixes=”exclude_prefix_list”>
any xml
</xsl:stylesheet>
ATTRIBUTE DESCRIPTION
The root element can’t have a parent, but it can have the child elements listed in
Table 13.2. Any xsl:include or xsl:import child elements must precede all other
child elements.
CHILD ELEMENT
xsl import
xsl include
xsl attribute-set
xsl decimal-format
(continues)
290 Chapter 13
CHILD ELEMENT
xsl key
xsl namespace-alias
xsl param
xsl:preserve space
xsl:strip space
xsl output
xsl template
xsl variable
Examples
Examples are provided of the xsl:stylesheet and xsl:transform elements act-
ing as root, as well as the use of an arbitrary root element. The basic stylesheet element
looks like this:
<xsl:stylesheet
xmlns:xsl = “http://www.w3.org/1999/XSL/Transform”
version = “1.0” >
. . . XSLT code and other XML
</xsl:stylesheet>
You might occasionally run across a stylesheet that has a root element like this:
<xsl:transform
xmlns:xsl = “http://www.w3.org/1999/XSL/Transform”
version = “1.0” >
. . . XSLT code and other XML
</xsl:transform>
<xsl:stylesheet
xmlns:xsl = “http://www.w3.org/1999/XSL/Transform”
version = “1.0”
extension-element-prefixes=””
exclude-element-prefixes=””>
XSLT code and other XML
</xsl:stylesheet>
xsl:output Syntax
The syntax of the xsl:output element, followed by a table describing the attributes,
is given here. As you can see from the syntax diagram, all the attributes are optional. If
all the attributes are omitted, the xsl:output element will have no effect.
<xsl:output
method = “xml” | “html” | “text” | “other”
version = “version_number”
encoding = “char_encoding”
omit-xml-declaration = “yes” | “no”
standalone = “yes” | “no”
doctype-public = “public_identifier”
doctype-system = “system_identifier”
cdate-section-elements = “elements_list”
indent = “yes” | “no”
media-type = “mime-type” />
ATTRIBUTE DESCRIPTION
Examples
The most common use of the output element is to choose whether xml, text, or html
is output. If you select HTML, XHTML tags such as <br /> will be converted back to
their early HTML form (e.g., <br>). This output element will tell the processor to do
the following:
You can also request that all XML tags be stripped entirely, leaving only the text
between the tags. This output method produces text only. It also outputs the text in the
iso-8859-1 character set:
The method is global—it changes the way your entire document is output. The
cdata-section-elements attribute works on specific elements. It will wrapper
any text as a cdata element. This action can be quite useful if an output is to be con-
sumed by an application that will interpret it as XML.
By default, the output method is XML. This doesn’t mean that Web browsers won’t
understand your result; rather, it just means the original XML structure of the docu-
ment is maintained. Regardless of whether it’s interpreted as HTML, you can make the
XML prettier by using the indent attribute, as follows:
Let’s say that you don’t want the output to be understood as some other mime
-type, such as Rich Text Format (RTF). You can do this by setting the media-type. In
such a case, you might not want the XML declaration to be at the top of the output. This
output element takes care of both concerns:
The remaining attributes are used to specify XML instructions. Here is an example
that uses all three of the remaining attributes:
<xsl:output method=”xml”
doctype-public=”-//MDT”
doctype-system=”http://www.ibiblio.org/mdthomas”
standalone=”no”/>
Templates
Templates are the key to XSLT. Without at least one of the elements presented here,
your stylesheet isn’t going to output much from your XML document. The xsl:
294 Chapter 13
xsl:template Syntax
The syntax for the xsl:template element, follows, and Table 13.4 describes this ele-
ment’s attributes. It is key to all of XSLT, and it occurs in most XSLT stylesheets. It
describes templates that are used to process XML nodes.
<xsl:template
match = “pattern”
name = “name”
priority = “number”
mode = “qname”>
. . .
</xsl:template>
ATTRIBUTE DESCRIPTION
xsl: attribute
xsl: choose
xsl: comment
xsl: copy
xsl: copy of
xsl: element
xsl: fallback
xsl: if
xsl: message
xsl: number
xsl: text
xsl: value of
xsl: variable
A value for the match attribute should be written in the abbreviated XPath syntax.
You’ll learn everything about XPath and the abbreviated XPath syntax later in this
chapter. Typically, the patterns used to describe a template are straightforward descrip-
tions of the position of nodes in the document hierarchy. For instance, “/”references
the root element; the”/page/rowset/row/*” references all the the children of row
elements. In terms of XSQL, this constitutes all the data fields.
It is possible for more than one template to match a given node in an XML document
and for the XSLT processor to choose between multiple templates. It chooses by first
looking at the specificity of the various templates. A more specific template is one that
more closely describes an XML node and is given more precedence over a less specific
296 Chapter 13
template. For instance, if there are two templates with the patterns, “/page/rowset/
row/*” and “/page/rowset/row/dept”, the second template would be applied to
the nodes “/page/rowset/row/dept”. In cases where multiple templates have the
same level of specificity, you would need to use the priority attribute to declare which
template should be preferred. If you don’t, the XSLT processor can consider it an error.
If the processor doesn’t consider it an error, the last template to appear in the document
is chosen.
The mode attribute simply gives you a way of labeling a template. If you have
two different templates but with the same pattern, you can give them different
modes. When you invoke the template, either through apply-templates or call
-template, you can specify the mode that you desire. The processor will select the
templates of the specified mode. In the absence of a mode attribute in the calling ele-
ment, XSLT decides on the template based on the rules of specificity and priority
described previously.
xsl:apply-templates Syntax
The syntax for the xsl:apply-templates element follows, and Table 13.6 describes
this element’s attributes. It directs the XSLT processor to apply templates for a specified
set of nodes. By default, the set of nodes comprises the children of the specified node.
<xsl:apply-templates
select = “XPath_expression”
mode = “mode”>
. . .
</xsl:apply-templates>
<xsl:apply-templates
select = “XPath_expression”
mode = “mode”/>
ATTRIBUTE DESCRIPTION
xsl: copy
xsl: element
xsl: fallback
xsl: for-each
xsl: if
xsl: message
xsl: otherwise
xsl: param
xsl: processing-instruction
xsl: template
xsl: variable
xsl: when
xsl: sort
xsl: with-param
xsl:call-template
A template can be invoked much like a subroutine with the xsl:call-template ele-
ment. Generally, this element is used in conjunction with one or more with-params
elements.
<xsl:call-template
name = “template_name”>
. . .
</xsl:call-template>
<xsl:call-template
name = “template_name”/>
The only parameter, name, is the name of the template that you wish to call. It is spec-
ified by that template’s name attribute. Table 13.8 lists the parent-child relationships.
298 Chapter 13
xsl: attribute
xsl: comment
xsl: copy
xsl: element
xsl: fallback
xsl: for-each
xsl: if
xsl: message
xsl: otherwise
xsl: param
xsl: processing-instruction
xsl: template
xsl: variable
xsl: when
xsl: with-param
When the template is called, the context node isn’t changed. Thus, any relative ref-
erences inside the template won’t work. A template referenced by apply-templates
can assume that the context node has switched to the XML node that matches the tem-
plate. However, this condition isn’t true for xsl:call-template. Instead, the refer-
ences in templates invoked by xsl:call-template should be absolute or involve
parameters that are passed in.
Examples
The simplest stylesheet that draws data from the source XML use a single apply
-template call:
<?xml version=”1.0”?>
<html xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” xsl:version=”1.0”
>
<head></head>
<body>
XSLT In-Depth 299
<p>
<xsl:apply-templates/>
</p>
</body>
</html>
This will output the values of all of the text nodes in the XML document. This is
rarely what you want, of course, but this example and the next one lend some insight
in to how apply-templates works. In this example, only the values for the ENAME
elements are returned. If there is no template associated with a particular node, an
apply-templates simply grabs the text of that node.
<?xml version=”1.0”?>
<html xmlns:xsl=”http://www.w3.org/1999/XSL/Transform” xsl:version=”1.0”
>
<head></head>
<body>
<xsl:apply-templates select=”/page/ROWSET/ROW/ENAME”/>
</body>
</html>
In the next example, you actually get something that looks nice. You define a tem-
plate that describes how you want the ENAME nodes to be presented. For this example,
they are in boldface and have been included as a list item. The bulleted list is defined
around the apply-templates element that is inside the page-level template.
<?xml version=”1.0”?>
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<html>
<head><title>Simple Stylesheet</title></head>
<body>
<h1>Simple Stylesheet</h1>
<ul>
<xsl:apply-templates select=”ROWSET/ROW/ENAME”/>
</ul>
</body>
</html>
</xsl:template>
<xsl:template match=”ENAME”>
<li><b><xsl:apply-templates/></b></li>
</xsl:template>
</xsl:stylesheet>
300 Chapter 13
Unlike our previous examples, you are now using a template element. This element
must be defined at the top level—a direct child of the root. For this reason, all the HTML
code should be included in a template. If you don’t include all this code, the XSLT
processor won’t find it. Generally, a template that matches the root of your XSQL docu-
ment includes scripts, as well as a head section and any header and footer information
The next example includes the remaining data from the query. Instead of a bulleted
list, the data is nicely formatted in a table. It also uses a call-template element to
put a standard break wherever necessary.
<?xml version=”1.0”?>
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<html>
<head><title>Simple Stylesheet</title></head>
<body>
<h1>Simple Stylesheet</h1>
<table border=”0”>
<th></th><th>Name</th><th>Job</th><th>Salary</th>
<xsl:apply-templates select=”ROWSET/*”/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match=”ROW”>
<tr>
<td><xsl:apply-templates select=”@num”/></td>
<td><xsl:apply-templates select=”ENAME”/></td>
<td><xsl:apply-templates select=”JOB”/></td>
<td><xsl:apply-templates select=”SAL”/></td>
<xsl:call-template name=”break”/>
</tr>
</xsl:template>
<xsl:template name=”break”>
<tr><td height=”4” colspan=”4”><hr/></td></tr>
</xsl:template>
<xsl:template match=”ENAME”>
<b><xsl:apply-templates/></b>
</xsl:template>
</xsl:stylesheet>
XSLT In-Depth 301
The template produces the output seen in Figure 13.1. The JOB and SAL elements
share a template, while ENAME gets its own. The only named template, break, is refer-
enced by xsl:call-template. (In practice, xsl:call-template is used in con-
junction with parameters. Examples are found later in this chapter.) Also, notice that
the num attribute from the ROW element is in our output. This was accomplished with
<xsl:apply-templates select=”@num”/>. Templates work against nodes, not
elements, so you can use these template elements in conjunction with attributes.
This example is a classic example of a push stylesheet. In the coming section on
loops, you’ll see how to get the same output by taking a pull approach.
Value Selection
You can’t write a meaningful template without selecting values from the source XML
at some point. Previously, to perform this task you used the xsl:apply-templates
element. It was a rather blunt instrument, though. The value-of element introduced
here gives you better control. This section shows you the syntax of this element and
works though some examples.
302 Chapter 13
ATTRIBUTE DESCRIPTION
xsl:value-of Syntax
The xsl:value-of element copies text from the XML document to the output. Gen-
erally, the text copied is value of a node, though it can also be an attribute of a node.
<xsl:value-of
select = “XPath-expression”
disable-output-escaping = “yes” | “no”./>
Table 13.9 lists the attributes. Table 13.10 lists the parent-child relationships.
xsl attribute
xsl comment
xsl copy
xsl element
xsl fallback
xsl for-each
xsl if
xsl message
xsl otherwise
xsl param
xsl processing-instruction
xsl template
xsl variable
xsl:when
XSLT In-Depth 303
Examples
If there is no template associated with a particular node, xsl:apply-templates will
provide the same functionality by default. However, it’s better to use xsl:value-of
for a couple of reasons. First, xsl:apply-templates will apply any template that
matches, which may not be what you want. There often might be a matching template,
but you really just want the value. Also, with xsl:value-of, you can disable output
escaping. The first example shows the places in our earlier stylesheet where
xsl:value-of should be used instead of xsl:apply-templates.
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<html>
<head><title>Simple Stylesheet</title></head>
<body>
<h1>Simple Stylesheet</h1>
<table border=”0”>
<th></th><th>Name</th><th>Job</th><th>Salary</th>
<xsl:apply-templates select=”ROWSET/*”/>
</table>
</body>
</html>
</xsl:template>
<xsl:template match=”ROW”>
<tr>
<td><xsl:value-of select=”@num”/></td>
<td><xsl:apply-templates select=”ENAME”/></td>
<td><xsl:apply-templates select=”JOB”/></td>
<td><xsl:apply-templates select=”SAL”/></td>
<xsl:call-template name=”break”/>
</tr>
</xsl:template>
<xsl:template name=”break”>
<tr><td height=”4” colspan=”4”><hr/></td></tr>
</xsl:template>
<xsl:template match=”ENAME”>
<b><xsl:value-of select=”.”/></b>
</xsl:template>
</xsl:stylesheet>
304 Chapter 13
The xsl:value-of element also gives you the ability to disable output escaping. If
you know that your query is going to return special characters, such as & and <, and
you don’t want them to be escaped, set disable-output-escaping=”yes”. Here
is an example XSQL page that returns an &:
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”value-of.xsl”?>
<page connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
select chr(‘38’) AS ampersand from dual
</xsql:query>
</page>
The ASCII code for the ampersand is 38, so this query will return a single ampersand
character. The following stylesheet demonstrates the difference between the default
behavior and the behavior when disable-output-escaping is set to “yes”.
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<html>
<head><title>Value-of Escape Example</title></head>
<body>
<h1>Value-of Escape Example</h1>
<ul>
<li>Default: <xsl:value-of select=”.”/></li>
<li>Output Escaping disabled: <xsl:value-of select=”.” disable-
output-escaping=”yes”/></li>
</ul>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Upon invoking the XSQL page through a browser, you’ll probably see an amper-
sand in both cases. But if you look at the source of the resultant HTML page, you’ll see
that the first ampersand is & the second, &.
Iteration
XSLT allows you to loop through a set of nodes with the xsl:for-each element. On
each iteration, the context-node changes to the next node in the set. Within the loop,
you can do a series of operations on the context-node, such as selecting data. Many
of the same goals that can be met with the xsl:for-each element can also be met
with the xsl:apply-templates element. Iteration is more familiar to a procedural
programmer than the tree-based recursive approach that xsl:apply-templates
entails. However, looping can lead to difficult-to-reuse XSLT code.
XSLT In-Depth 305
Xsl:for-each Syntax
The xsl:for-each element declares the beginning of a loop. The loop iterates over
all the nodes in the node set described by the value of the select attribute, which is
an XPath-expression.
<xsl:for-each
select = “XPath_expression”>
any XML
</xsl:for-each>
The sole attribute—select—describes a node set over which the iteration should
occur. Table 13.11 lists the parent-child relationships.
xsl:attribute xsl:apply-imports
xsl:comment xsl:apply-templates
xsl:copy xsl:attribute
xsl:element xsl:call-template
xsl:fallback xsl:choose
xsl:for-each xsl:comment
xsl:if xsl:copy
xsl:message xsl:copy-of
xsl:otherwise xsl:element
xsl:param xsl:fallback
xsl:processing-instruction xsl:for-each
xsl:template xsl:if
xsl:variable xsl:message
xsl:when xsl:number
xsl:processing-instruction
xsl:sort
xsl:text
xsl:value-of
xsl:variable
306 Chapter 13
The for-each element can be used almost anywhere in XSLT except as a top-level
element. One xsl:for-each element can be nested inside another element, and mul-
tiple levels of nesting are permitted.
Examples
In our example of xsl:apply-templates and xsl:value-of, you saw how to
create a stylesheet by matching templates to nodes and then using xsl:apply
-templates to match the input XML nodes with the templates in the stylesheet. This
is the push approach described earlier. For this example, you’ll see how to get the same
stylesheet using iteration instead.
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<html>
<head><title>Simple Stylesheet</title></head>
<body>
<h1>Simple Stylesheet</h1>
<table border=”0”>
<th></th><th>Name</th><th>Job</th><th>Salary</th>
<xsl:for-each select=”ROWSET/ROW”>
<tr>
<td><xsl:value-of select=”@num”/></td>
<td><b><xsl:value-of select=”ENAME”/></b></td>
<td><xsl:value-of select=”JOB”/></td>
<td><xsl:value-of select=”SAL”/></td>
<xsl:call-template select=”break”/>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template name=”break”>
<tr><td height=”4” colspan=”4”><hr/></td></tr>
</xsl:template>
</xsl:stylesheet>
In this example, the formatting of the individual data elements is done within the ele-
ments. If you wish, you can still use templates in conjunction with an xsl:for-each
loop:
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
XSLT In-Depth 307
<xsl:template match=”page”>
<html>
<head><title>Simple Stylesheet</title></head>
<body>
<h1>Simple Stylesheet</h1>
<table border=”0”>
<th></th><th>Name</th><th>Job</th><th>Salary</th>
<xsl:for-each select=”ROWSET/ROW”>
<tr>
<td><xsl:value-of select=”@num”/></td>
<td><xsl:apply-templates select=”ENAME”/></td>
<td><xsl:apply-templates select=”JOB”/></td>
<td><xsl:apply-templates select=”SAL”/></td>
<xsl:call-template name=”break”/>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template name=”break”>
<tr><td height=”4” colspan=”4”><hr/></td></tr>
</xsl:template>
<xsl:template match=”ENAME”>
<b><xsl:value-of select=”.”/></b>
</xsl:template>
</xsl:stylesheet>
In this case, the three apply-templates elements are applied to each ROW element
that iterates through the list. Likewise, any of those templates could have its own
for-each element inside of it.
Conditional Logic
As with most computer programming languages, you can use conditional logic to
decide what the output should be based on the input. You have the xsl:if element
and the xsl:choose/xsl:when/xsl:otherwise elements at your disposal.
These last three elements are grouped together because they are used collectively for
if-else-if-else processing. However, the xsl:if element adds a set of actions
only if its condition is true. Unlike most programming languages, it can’t be used
308 Chapter 13
in conjunction with an else or else if construct. This section looks at the syntax of
these four elements and provides examples of how to use them in templates. Before
you cover the element syntax, you need to understand a boolean expression as it is
defined by XSLT.
boolean Expressions
Used by conditional logic elements. boolean expressions evaluate how XSLT should
proceed. If the expression evaluates to true, the XSLT inside the element will be output
and the other text output as appropriate. If the expression evaluates to false, the ele-
ment will be skipped. XSLT determines truth and falseness by evaluating the expres-
sion and then using the rules of the XPath boolean()function on the results. These boil
down to the following:
If the expression evaluates to a boolean expression, that value will be used to
determine truth and falseness. This is the simplest case.
If the expression evaluates to a number, it will be true if it is neither 0 nor
NotANumber.
If the expression evaluates to a node set, it will be true if it isn’t empty.
If the expression evaluates to a string, it will be true if it isn’t empty.
You’ll be learning more about XPath as you progress through the chapter. XPath
provides a variety of functions that can be used not only for boolean expressions but
for all types of expressions.
xsl:if Syntax
The xsl:if element allows you to conditionally include the contents of the element.
<xsl:if
test = “boolean_expression”>
XML
XSLT Template Elements
</xsl:if>
It has one attribute, test, which is a boolean expression that evaluates to either
true or false. As discussed in an earlier section, this doesn’t mean that the expression
must evaluate to either true or false. If it evaluates to a node set, a number, or a string,
the rules described in the earlier section on boolean expressions will apply. Table
13.12 lists the parent-child relationships.
If the boolean expression evaluates to true, any text or XML contained in the ele-
ment will be output after any XSLT elements have been evaluated. There is no “else”
element that can be used with xsl:if. If you need to do compound conditional pro-
cessing, you should use the xsl:choose element instead.
XSLT In-Depth 309
xsl:attribute xsl:apply-imports
xsl:comment xsl:apply-templates
xsl:copy xsl:attribute
xsl:element xsl:call-template
xsl:fallback xsl:choose
xsl:for-each xsl:comment
xsl:if xsl:copy
xsl:message xsl:copy-of
xsl:otherwise xsl:element
xsl:param xsl:fallback
xsl:processing-instruction xsl:for-each
xsl:template xsl:if
xsl:variable xsl:message
xsl:when xsl:number
xsl:processing-instruction
xsl:text
xsl:value-of
xsl:variable
<xsl:choose>
<xsl:when test=”boolean_expression”>
XML text
Template XSLT Elements
</xsl:when>
310 Chapter 13
. . .
<xsl:otherwise>
XML text
Template XSLT Elements
</xsl:otherwise>
</xsl:choose>
The xsl-when has one attribute, test, which is a boolean expression that evaluates
to either true or false. As discussed in an earlier section, this doesn’t mean that the
expression must evaluate to either true or false. If it evaluates to a node set, a number,
or a string, the rules described in the earlier section on boolean expressions will apply.
The only valid parent for xsl:when or xsl:otherwise is xsl:choose. Children
of xsl:choose must be xsl:when or xsl:otherwise. Table 13.13 details the
parent-child relationships of the triumvirate.
The otherwise element is never required to be a child of xsl:choose. When it is
used, it acts like an else clause in an if-then-else structure. If no when condition
is met, the default otherwise element is applied. You can include as many xsl:when
elements as you want.
xsl:attribute xsl:apply-imports
xsl:comment xsl:apply-templates
xsl:copy xsl:attribute
xsl:element xsl:call-template
xsl:fallback xsl:choose
xsl:for-each xsl:comment
xsl:if xsl:copy
xsl:message xsl:copy-of
xsl:otherwise xsl:element
xsl:param xsl:fallback
xsl:processing-instruction xsl:for-each
xsl:template xsl:if
xsl:variable xsl:message
XSLT In-Depth 311
xsl:when xsl:number
xsl:processing-instruction
xsl:text
xsl:value-of
xsl:variable
Examples
These examples cover the two different conditional structures provided by XSLT. The
xsl:if element only has an effect when the condition is met, while the xsl:choose
structure can affect a variety of different behaviors based on a number of different con-
ditions. Thus, the xsl:choose structure tends to be more interesting and useful. The
first stylesheet performs a simple test on the salary node and only displays those with
a salary higher than 1,000.
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<html>
<head><title>Simple Stylesheet</title></head>
<body>
<h1>Simple Stylesheet</h1>
<table border=”1”>
<th></th><th>Name</th><th>Job</th><th>Salary</th>
<xsl:for-each select=”ROWSET/ROW”>
<xsl:if test=”number(SAL)>1000”>
<xsl:apply-templates select=”.”/>
</xsl:if>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template match=”ROW”>
<tr>
312 Chapter 13
<td><xsl:value-of select=”@num”/></td>
<td><xsl:apply-templates select=”ENAME”/></td>
<td><xsl:apply-templates select=”JOB”/></td>
<td><xsl:apply-templates select=”SAL”/></td>
</tr>
</xsl:template>
<xsl:template match=”ENAME”>
<b><xsl:value-of select=”.”/></b>
</xsl:template>
</xsl:stylesheet>
The choose structure allows you to specify alternatives when a specific condition
isn’t met. This stylesheet displays all employees and highlights those that have a salary
over 1,000. It also makes use of the mode attribute of the apply-templates and
template elements.
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<html>
<head><title>Simple Stylesheet</title></head>
<body>
<h1>Simple Stylesheet</h1>
<table border=”0”>
<th></th><th>Name</th><th>Job</th><th>Salary</th>
<xsl:for-each select=”ROWSET/ROW”>
<xsl:choose>
<xsl:when test=”number(SAL)>2000”>
<xsl:apply-templates select=”.” mode=”dark”/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select=”.” mode=”light”/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</table>
</body>
XSLT In-Depth 313
</html>
</xsl:template>
</td>
<td><xsl:apply-templates select=”JOB”/></td>
<td><xsl:apply-templates select=”SAL”/></td>
</tr>
</xsl:template>
<xsl:template match=”ENAME”>
<b><xsl:value-of select=”.”/></b>
</xsl:template>
</xsl:stylesheet>
The output of this template is shown in Figure 13.2. For this example, two templates
are used with two different modes—light and dark. The xsl:choose structure
decides which mode to use based on salary. Doing it this way is a bit redundant since
there is only one attribute to change. However, outputting data inside xml elements,
such as attributes, is a little tricky. You’ll learn more in the next section.
Also, note that an xsl:if element is used inside the light template. If the sal is
less than 1,000, the word “poor” will be added. Note that ”<” had to be used
instead of “<”, because “<”is reserved in XML.
314 Chapter 13
<xsl:element
name = “xml_element_name”
namespace =”namespace_URI”
use-attribute-sets = “attribute_set_names”>
xsl:attribute elements
template xslt elements, other xml, and text
</xsl:element>
Any xsl:attribute element defines an attribute for the element and. Anything
else between the start and end xsl:element tags will appear as the value in the
outputted element. The attributes for the xsl:element element are described in
Table 13.14.
Table 13.15 lists the parent-child relationships.
ATTRIBUTE DESCRIPTION
xsl:copy xsl:apply-imports
xsl:element xsl:apply-templates
xsl:fallback xsl:attribute
xsl:for-each xsl:call-template
xsl:if xsl:choose
xsl:message xsl:comment
xsl:otherwise xsl:copy
xsl:param xsl:copy-of
xsl:template xsl:element
xsl:variable xsl:fallback
xsl:when xsl:for-each
xsl:if
xsl:message
xsl:number
xsl:processing-instruction
xsl:text
xsl:value-of
xsl:variable
xsl:attribute Syntax
The xsl:attribute element defines an attribute-value pair for its parent node. Its
syntax is as follows:
<xsl:attribute
name =”attribute_name”
namespace =”namespace_name”>
attribute_value
</xsl:attribute>
ATTRIBUTE DESCRIPTION
xsl:attribute-set xsl:apply-imports
xsl:copy xsl:apply-templates
xsl:element xsl:call-template
xsl:fallback xsl:choose
xsl:for-each xsl:copy
xsl:if xsl:copy
xsl:message xsl:copy-of
xsl:otherwise xsl:fallback
xsl:param xsl:for-each
xsl:template xsl:if
xsl:variable xsl:message
xsl:when xsl:message
xsl:number
xsl:text
xsl:value-of
xsl:variable
318 Chapter 13
ATTRIBUTE DESCRIPTION
xsl:attribute-set Syntax
The xsl:attribute-set element allows you to define a static set of elements that
can be reused. This keeps you from having to repeatedly define the exact same set of
xsl:attribute elements. The syntax is as follows:
<xsl:attribute-set
name = “set_name”
use-attribute-sets = “attribute_set_names”>
xsl:attribute elements
</xsl:attribute-set>
<?xml version=”1.0”?>
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<html>
<head><title>Simple Stylesheet</title></head>
<body>
<h1>Simple Stylesheet</h1>
<ul>
<xsl:apply-templates select=”ROWSET/ROW”/>
</ul>
</body>
</html>
</xsl:template>
XSLT In-Depth 319
<xsl:template match=”ROW”>
<li><b>
<xsl:element name=”a”>
<xsl:attribute name=”href”>editEmployee?<xsl:value-of
select=”EMPNO”/></xsl:attribute>
<xsl:attribute name=”rel”>next</xsl:attribute>
<xsl:attribute name=”rev”>prev</xsl:attribute>
<xsl:value-of select=”ENAME”/>
</xsl:element>
</b></li>
</xsl:template>
</xsl:stylesheet>
In this example, the rel and the rev elements don’t take dynamic inputs. They are
ideal candidates to be an attribute-set. Then, if you need to reuse the same set of
attributes somewhere else in your document, you’ll have a better mechanism than
copy and paste. The first step is to set up the attribute-set element:
<xsl:attribute-set name=”empEditorLink”>
<xsl:attribute name=”rel”>next</xsl:attribute>
<xsl:attribute name=”rev”>prev</xsl:attribute>
</xsl:attribute-set>
You’ll need to define the attribute-set as a child of the root element, since it is
the only valid parent for an xsl:attribute-set element. Now, you can recreate the
row template as follows:
<xsl:template match=”ROW”>
<li><b>
<xsl:element name=”a” use-attribute-sets=”empEditorLink”>
<xsl:attribute name=”href”>editEmployee?<xsl:value-of
select=”EMPNO”/></xsl:attribute>
<xsl:value-of select=”ENAME”/>
</xsl:element>
</b></li>
</xsl:template>
The attribute element can also be used to create attributes on any arbitrary element
in your stylesheet. This is a different way to accomplish the same goal of setting attrib-
utes. Instead of creating a new element, the attribute is set on the existing XML element.
<xsl:template match=”ROW”>
<li><b>
<a>
<xsl:attribute name=”href”>editEmployee?<xsl:value-of
select=”EMPNO”/></xsl:attribute>
<xsl:value-of select=”ENAME”/>
</a>
</b></li>
</xsl:template>
320 Chapter 13
xsl:text Syntax
The xsl:text element is used to insert text verbatim into the document:
<xsl:text
disable-output-escaping = “yes” | “no”>
string data
</xsl:text>
xsl:attribute
xsl:comment
xsl:copy
xsl:element
xsl:fallback
xsl:for-each
xsl:if
xsl:message
xsl:otherwise
xsl:param
xsl:processing-instruction
xsl:template
xsl:variable
xsl:when
XSLT In-Depth 321
<pre>
<xsl:text>
W o w ! ! ! ! W h i t e s p a c e ! ! !
</xsl:text>
<xsl:text disable-output-escaping=”yes”>
disable-output-escaping tag: <aTag/>
</xsl:text>
</pre>
xsl:comment Syntax
The xsl:comment element generates an XML comment in the output:
<xsl:comment>
XSLT and XML
</xsl:comment>
xsl:copy xsl:apply-imports
xsl:element xsl:apply-templates
xsl:fallback xsl:call-templates
xsl:for-each xsl:choose
(continues)
322 Chapter 13
xsl:if xsl:copy
xsl:message xsl:copy-of
xsl:otherwise xsl:fallback
xsl:param xsl:for-each
xsl:template xsl:if
xsl:variable xsl:message
xsl:when xsl:number
xsl:text
xsl:value-of
xsl:variable
The start tag is replaced with <!— and the end tag is replaced with —>. Here is an
example:
xsl:copy Syntax
The xsl:copy element allows you to copy the current node. It won’t copy the attrib-
utes or child elements.
<xsl:copy
use-attribute-sets = “attribute_set_list”>
</xsl:copy>
xsl:comment xsl:apply-imports
xsl:copy xsl:apply-templates
xsl:element xsl:attribute
xsl:fallback xsl:call-template
xsl:for-each xsl:choose
xsl:if xsl:comment
xsl:message xsl:copy
xsl:otherwise xsl:copy-of
xsl:param xsl:element
xsl:processing-instruction xsl:fallback
xsl:template xsl:for-each
xsl:variable xsl:if
xsl:when xsl:message
xsl:number
xsl:processing-instruction
xsl:text
xsl:value-of
xsl:variable
The following example uses the xsl:copy element to copy only those rows of the
clerk employees:
<?xml version=”1.0”?>
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<xsl:element name=”ROWSET”>
<xsl:apply-templates select=”ROWSET/ROW”/>
</xsl:element>
</xsl:template>
324 Chapter 13
<xsl:template match=”ROW”>
<xsl:if test=”JOB=’CLERK’”>
<xsl:copy>
<xsl:for-each select=”@*”>
<xsl:copy/>
</xsl:for-each>
<xsl:apply-templates select=”ENAME | SAL”/>
</xsl:copy>
</xsl:if>
</xsl:template>
xsl:copy-of Syntax
The xsl:copy-of element allows you to copy the tree fragment specified by the
select attribute:
<xsl:copy-of
select = “XPath_expression” />
The single attribute, select, is an expression that should evaluate to a node set.
The members of the node set and their children will be copied. Table 13.22 lists the
parent-child relationships.
xsl:attribute
xsl:comment
xsl:copy
xsl:element
xsl:fallback
xsl:for-each
xsl:if
XSLT In-Depth 325
xsl:if
xsl:message
xsl:otherwise
xsl:param
xsl:processing-instruction
xsl:template
xsl:variable
xsl:when
Here is a simple example that creates a new XML document containing only those
employees who are clerks:
<?xml version=”1.0”?>
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<xsl:apply-templates select=”ROWSET/ROW”/>
</xsl:template>
<xsl:template match=”ROW”>
<xsl:if test=”JOB=’CLERK’”>
<xsl:copy-of select=”.”/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
xsl:namespace-alias Syntax
The xsl:namespace-alias allows you translate one namespace in the input to
another namespace in the output:
<xsl:namespace-alias
stylesheet-prefix = “prefix” | “#default”
result-prefix = “prefix” | “#default” />
ATTRIBUTE DESCRIPTION
If the string “#default” is used for either attribute, it would represent the default
namespace. This is either no namespace declared by xmlns or no namespace at all.
The xsl:namespace-alias element must be the child of the root element and
may not have child elements.
Your stylesheets can have multiple xsl:namespace-alias elements. However,
it is an error to have multiple xsl:namespace-alias elements that have the
same stylesheet-prefix, and it may produce conflicts to have multiple xsl:
namespace-alias elements that have the same result-prefix elements.
xsl:processing-instruction Syntax
The xsl:processing-instruction allows you to specify an xml processing
instruction. It can’t be used to specify the xml declaration; for that, you can use the
xsl:output element.
<xsl:processing-instruction
name = “processing_instruction_name”>
xslt template elements and other text & xml
</xsl:processing-instruction>
The name attribute specifies the name of the processing instruction. Table 13.24 lists
the parent-child relationships.
xsl:copy xsl:apply-imports
xsl:element xsl:apply-templates
xsl:fallback xsl:call-template
xsl:for-each xsl:choose
XSLT In-Depth 327
xsl:if xsl:copy
xsl:message xsl:copy-of
xsl:otherwise xsl:fallback
xsl:param xsl:for-each
xsl:template xsl:if
xsl:variable xsl:message
xsl:when xsl:text
xsl:value-of
xsl:variable
<xsl:processing-instruction name=”xml-stylesheet”>
type=”text/xml” href=”someStylesheet.xsl”
</xsl:processing-instruction>
Numbering Elements
There are two XSLT elements that concern numbering. The first, xsl:number, is used
to provide a formatted number that details the position of a node in a node list. The
second, xsl:decimal-format, is used in conjunction with the format-number
function that will be covered in the “XPath” section.
328 Chapter 13
xsl:number
The xsl:number element inserts a formatted number into the output. Typically,
this element is used to provide numbering for a set of XML elements in the source
document.
<xsl:number
level = “single” | “multiple” | “any”
count = “pattern”
from = “pattern”
value = “number_expression”
format = “format_mask”
lang = “nmtoken”
letter-value = { “alphabetic” | “traditional” }
grouping-separator = “char”
grouping-size = “number” />
The attributes of elements control how the numbering is done. By default, the num-
ber element produces the next number in sequence each time the XSLT processor
encounters the particular element. The attributes enhance this basic behavior. Table
13.25 lists the attributes.
ATTRIBUTE DESCRIPTION
ATTRIBUTE DESCRIPTION
xsl:attribute
xsl:comment
xsl:copy
xsl:element
xsl:fallback
xsl:for-each
xsl:if
xsl:message
xsl:otherwise
xsl:param
xsl:processing-instruction
xsl:template
xsl:variable
xsl:when
330 Chapter 13
xsl:decimal-format Syntax
The xsl:decimal-format element is used in conjunction with the format
-number() function. It defines a named decimal format that can be referenced by the
format-number() function when it translates floating-point numbers for output.
<xsl:decimal-format
name = “decimal_format_name”
decimal-separator = “decimal_separator”
grouping-separator = “grouping_separator”
infinity = “infinity_string”
minus-sign = “minus_char”
NaN = “NaN_string”
percent = “percent_char”
per-mille = “per_mille”
zero-digit = “zero_digit”
digit = “digit”
pattern-separator = “pattern_separator” />
ATTRIBUTE DESCRIPTION
xsl:variable Syntax
An xsl:variable actually behaves like a constant—once its value is set, it can’t be
changed:
<xsl:variable
name=”variable_name”>
. . .
</xsl:variable>
<xsl:variable
name=”variable_name”
select=”expression”/>
The name attribute is the name of the variable. If the select attribute is specified, the
result of the expression will be the value for the param, and the xsl:param element
should have the empty-element form. Table 13.28 lists the parent-child relationships.
xsl:attribute xsl:apply-imports
xsl:comment xsl:apply-templates
xsl:copy xsl:attribute
xsl:element xsl:call-template
xsl:fallback xsl:choose
xsl:for-each xsl:comment
xsl:if xsl:copy
xsl:message xsl:copy-of
xsl:otherwise xsl:element
xsl:param xsl:fallback
(continues)
332 Chapter 13
xsl:processing-instruction xsl:for-each
xsl:stylesheet xsl:if
xsl:template xsl:message
xsl:transform xsl:number
xsl:variable xsl:processing-instruction
xsl:when xsl:text
xsl:value-of
xsl:variables
Variable Examples
The xsl:variable is typically used to grab information from the source XML that
will be reused repeatedly throughout the stylesheet or to store information. Once you
grab the information, you can use it again and again by referencing the variable name
with a $ prefix, as in $variable_name. A variable’s value is set as part of its definition,
making it quite different from the variables you use in other programming languages.
Those variables must be defined and their value must be changed many times over the
life of the programs in which they are found. In this way, variables act like constants.
You can define a variable either at the stylesheet level or at the template-body level.
When a variable is defined at the stylesheet level, it has global scope—you can use it
anywhere in the document. When it is defined at the template-body level, it can be
used only if it is referenced in following siblings or descendants of following siblings.
This is similar to how local variables work in procedural languages—they aren’t avail-
able outside of the subroutine. In other words, they aren’t available outside the ele-
ment in which they are created, and they are available only inside the element after
they have been declared.
Variables at different levels of scope can have the same name. If there are conflicts,
the most narrowly scoped variable takes precedence. For instance, the variable
row_num defined inside of a template preempts the variable row_num defined at the
stylesheet level.
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:variable name=”row_num” select=”0”/>
<xsl:template match=”page”>
XSLT In-Depth 333
<html>
<head><title>Simple Stylesheet</title></head>
<body>
<h1>Simple Stylesheet</h1>
<table border=”0”>
<th align=”left”>Name</th>
<th align=”left”>@num</th>
<th align=”left”>global variable</th>
<th align=”left”>local variable</th>
<xsl:for-each select=”ROWSET/ROW”>
<tr>
<td align=”left” width=”200”>
<xsl:value-of select=”ENAME”/>
</td>
<td align=”left” width=”200”>
<xsl:value-of select=”@num”/>
</td>
<td align=”left” width=”200”>
<xsl:value-of select=”$row_num”/>
</td>
<xsl:variable name=”row_num” select=”@num”/>
<td align=”left” width=”200”>
<xsl:value-of select=”$row_num”/>
</td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
The results of this example are shown in Figure 13.3. The first time $row_num is
referenced, it takes the value of the stylesheet-level row_num variable because no
template-body-level $row_num exists at that point. The second time it is referenced, it
takes the value of the attribute “num” of the current row.
The output seems to contradict something you may have read about XSLT—that
you can set the value of a variable only once. After all, the local variable row_num
seems to have been set 14 times! This doesn’t make the previous statement false, how-
ever. It’s more precise to say that a variable’s value is set upon its definition. When
xsl:variable seems to be changing, it is really just being redefined. If the
xsl:variable element—and thus its value definition—is evaluated more than once,
the variable may have different values on each instantiation. This is the case here.
334 Chapter 13
In this example, the variable took a string value. Variables can also take a number, a
boolean, or a node-set value. Because they can take a node-set value, variables are often
used to store the context node at any particular time during processing. In the following
example, variables are used to capture the context node at three different levels—before
going into the outer for-each loop, before going into the inner for-each loop, and at
the lowest level of the document.
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<html>
<head><title>Simple Stylesheet</title></head>
<body>
<h1>Simple Stylesheet</h1>
<table border=”0”>
<th>Top level Node</th>
<th>Mid Node</th>
<th>Lowest Node</th>
XSLT In-Depth 335
</xsl:stylesheet>
This example makes use of the name function, which you’ll learn more about later
in this chapter.
xsl:param Syntax
The xsl:param and xsl:variable elements are closely related and share essen-
tially the same syntax:
<xsl:param
name=”param_name”>
. . .
</xsl:param>
<xsl:param
name=”param_name”
select=”expression”/>
The name attribute is the name of the parameter. If the select attribute is specified,
the result of the expression will be the value for the param, and the xsl:param ele-
ment should have the empty-element form.
<xsl:variable
name=”variable_name”
select=”expression”>
. . .
</xsl:variable>
xsl:template xsl:apply-templates
xsl:attribute
xsl:call-template
xsl:choose
xsl:comment
xsl:copy
xsl:copy-of
xsl:element
xsl:fallback
xsl:for-each
xsl:if
xsl:message
xsl:number
xsl:processing-instruction
xsl:text
xsl:value-of
xsl:variable
xsl:with-param Syntax
The xsl:with-param element is used in conjunction with the xsl:apply-templates
and xsl:call-template elements to pass parameters to a template. The template
should have a xsl:template of the same name already defined inside the template.
<xsl:with-param
name=”param-name”>
. . .
</xsl:with-param>
<xsl:with-param
name=”param-name”
select=”expression”/>
Name is a required attribute. If select is an attribute, then the element should have
the empty-element form. Table 13.30 lists the parent-child relationships.
XSLT In-Depth 337
xsl:apply-templates xsl:apply-imports
xsl:call-template xsl:apply-templates
xsl:attribute
xsl:call-template
xsl:choose
xsl:comment
xsl:copy
xsl:copy-of
xsl:element
xsl:fallback
xsl:for-each
xsl:if
xsl:message
xsl:number
xsl:processing-instruction
xsl:text
xsl:value-of
xsl:variable
Parameter Examples
Parameters perform like variables in many cases, but they are used mainly to pass argu-
ments to templates. You use xsl:with-param with the xsl:apply-templates and
xsl:call-template elements to pass the chosen argument. Inside the template, you
have an xsl:parameter element that receives the parameter. The xsl:with-param
element that is used to pass an argument into a template must have the same name as the
corresponding xsl:parameter element inside the template. If the xsl:with-param
doesn’t match any xsl:parameter elements inside the target template, it would be
simply ignored. For an xsl:parameter element, you can also provide a default value
to use in case no xsl:with-param element is included in the call.
In an earlier example, you highlighted the high-salary employees to demonstrate
the conditional processing of XSLT. In that case, there were two entirely different tem-
plates that did essentially the same thing and differed only in color. The following
338 Chapter 13
example shows how you can make your code cleaner by using parameters. It also
reworks the call template so that it alternates on every other color.
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<xsl:processing-instruction name=”xml-stylesheet”>type=”text/xml”
href=”someStylesheet.xsl”</xsl:processing-instruction>
<html>
<head><title>Simple Stylesheet</title></head>
<body>
<h1>Simple Stylesheet</h1>
<table border=”0”>
<th></th><th>Name</th><th>Job</th><th>Salary</th>
<xsl:for-each select=”ROWSET/ROW”>
<xsl:choose>
<xsl:when test=”number(SAL)>2000”>
<xsl:apply-templates select=”.”>
<xsl:with-param name=”bgcolor”>gray</xsl:with-param>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select=”.”/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
<xsl:template match=”ROW”>
<xsl:param name=”bgcolor”>white</xsl:param>
<tr>
<xsl:attribute name=”bgcolor”>
<xsl:value-of select=”$bgcolor”/>
</xsl:attribute>
<td><xsl:value-of select=”@num”/></td>
<td><xsl:apply-templates select=”ENAME”/></td>
<td><xsl:apply-templates select=”JOB”/></td>
<td><xsl:apply-templates select=”SAL”/></td>
</tr>
<xsl:choose>
<xsl:when test=”@num mod 2 = 1”>
<xsl:call-template name=”break”>
<xsl:with-param name=”break_color”>red</xsl:with-param>
XSLT In-Depth 339
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name=”break”/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name=”break”>
<xsl:param name=”break_color”>blue</xsl:param>
<tr><td height=”4” colspan=”4”>
<xsl:attribute name=”bgcolor”>
<xsl:value-of select=”$break_color”/>
</xsl:attribute>
</td>
</tr>
</xsl:template>
<xsl:template match=”ENAME”>
<b><xsl:value-of select=”.”/></b>
</xsl:template>
</xsl:stylesheet>
Reusing Stylesheets
As you develop stylesheets, you’ll probably find yourself addressing some of the same
problems over and over again. If you start looking at XSLT with the eye of an experi-
enced programmer, you’ll probably wonder whether there is any way to modularize
and reuse your code. There is. The xsl:include and xsl:import elements allow
you to use XSLT that is defined in other stylesheets. These elements, combined with
variables and parameters, allow you to reuse individual templates and declarations.
When you use the xsl:include element, it’s as if you’ve copied and pasted
another stylesheet exactly where the xsl:include element is placed. In the case of
name conflicts, the order of precedence of XSLT determines which element should be
used based on the element’s position in the document after the include. (In other
words, the fact that the template was included from another stylesheet makes no dif-
ference when determining which element to use.)
The xsl:import element works differently. In the case of conflicts, the elements in
the importing stylesheet always take precedence over the elements in the imported
stylesheet. If you specifically want to use a template in an imported stylesheet, you
must use the xsl:apply-imports element.
340 Chapter 13
The href attribute specifies the URI of the stylesheet that you wish to import or
include, respectively. They can be the children of the top-level element only, and they can
have no children. If they are used, they must be the first children of the root element.
Specifically, no child element of the root can precede xsl:import except xsl:include,
and no child element of the root can precede xsl:include except xsl:import.
xsl:apply-imports
This element is used to override the default behavior of XSLT concerning imported
stylesheets. By default, an imported stylesheet template is used only if there are no tem-
plates of the same name defined in the main stylesheet. With xsl:apply-imports,
you can specifically request that an imported template be applied from within a template
of the same name.
<xsl:apply-imports/>
This element can’t have children, and the only valid parent is xsl:template.
Sorting
XSLT gives you a mechanism for sorting data. Of course, you can also sort at the SQL
level. Generally speaking, it is more efficient to sort at the SQL level than at the XSLT
level. There will be cases, though, in which you will need to sort by using XSLT. This
section defines the syntax for the xsl:sort element and gives examples of it’s use.
xsl:sort Syntax
The xsl:sort element changes the default sorting. By default, elements are presented
in document order. However the elements are sequenced in the document in the order
in which they are output. The xsl:sort element allows the order to be changed to
some other kind of sorting, typically alphabetic or numeric and based on some prop-
erty of the elements to be sorted.
<xsl:sort
select = “sort_expression”
lang = “”
data-type = “text” | “number” | “other_processor_supported_type”
order = “ascending” | “descending”
case-order = “upper-first” | “lower-first” />
ATTRIBUTE NAME
Examples
The most important thing to remember when using the xsl:sort element is that
numeric sorts aren’t the same as alphanumeric sorts. Since the number 1 precedes the
number 2, 1,000 will precede 2 in an alphanumeric sort. In the following example in
which a sort is done by salary, the data type is set to number. In a numeric sort, of
course, 1,000 follows 2. This sort is also a multiple key sort because two different sort
elements are specified.
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”page”>
<html>
<head><title>Simple Stylesheet</title></head>
<body>
<h1>Simple Stylesheet</h1>
<table border=”0”>
<th></th><th>Name</th><th>Job</th><th>Salary</th>
<xsl:for-each select=”ROWSET/ROW”>
<xsl:sort select=”JOB”/>
<xsl:sort select=”SAL”
order=”descending”
data-type=”number”/>
342 Chapter 13
<tr>
<td><xsl:value-of select=”@num”/></td>
<td><b><xsl:value-of select=”ENAME”/></b></td>
<td><xsl:value-of select=”JOB”/></td>
<td><xsl:value-of select=”SAL”/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Whitespace Handling
You can define how whitespace should be handled for elements by using the
xsl:preserve-whitespace and xsl:strip-whitespace elements. The
xsl:preserve-whitespace and xsl:strip-whitespace elements are closely
related and share essentially the same syntax. Both are top-level elements without chil-
dren, and both must be children of the root element.
Miscelleneous Elements
These last two elements don’t fit in particularly well anywhere else. The first,
xsl:key, defines search patterns for elements and is used in conjunction with the
key() function discussed later. The second, xsl:message, provides a way to send
messages to the XSLT processor, while the third is used with XSLT extensions.
xsl:key Syntax
The xsl:key element creates identifiers for elements. A key allows you to easily select
one or more elements based on the value of an attribute or the value of the element. It
is a top-level element that can be a child of the root element only, and it has no children.
XSLT In-Depth 343
ATTRIBUTE DESCRIPTION
There may be more than one key element per stylesheet, but they should all have
unique names. A key is used in conjunction with the key() function.
<xsl:key
name = “key_name”
match = “nodeset_pattern”
use = “node_expression” />
xsl:message
The xsl:message element method provides a mechanism to communicate messages
to the XSLT processor. What the XSLT processor does with these messages is processor-
dependent.
<xsl:message
terminate = “yes” | “no”>
message
</xsl:message>
xsl:fallback
The xsl:fallback element works with extension elements that aren’t defined in
XSLT 1.0. It defines what to do if an extension that you are trying to use is unknown or
unavailable to the XSLT processor.
<xsl:fallback>
Template XSLT
</xsl:fallback>
XPath
XPath is a language designed to locate nodes in an XML document. The simple paths
and expressions that you’ve used thus far are all examples of XPath in action. In many
cases, its syntax is similar to that used by file systems. This isn’t coincidental—both
XML documents and file systems are tree-based. However, XPath goes a bit beyond the
simple path notation you’ve seen so far. It has, for example, its own data types and
built-in functions. It also has a rich tool set of axes and predicates; the axes allow you
to navigate and grab nodes across the XML tree, and the predicates allow you to filter
the nodes that you grab.
This section covers XPath by looking first at the different data types that XPath sup-
ports. Then you’ll learn formally about expressions and paths, with which, from the
previous section, you already have experience. This section concludes with a discus-
sion of axes and predicates. The built-in functions are discussed in the final section of
this chapter.
■■ Multiplication (*)
■■ Division (div)
■■ Modulus (mod)
There are several other mathematical functions, covered later in this chapter, that you
can use.
XPath strings are composed of Unicode characters by default. Since XPath is a text-
focused language, a lot of the work that you do deals with strings. The core functions
include several string functions that give you a rich set of functionality. With a little
work, you can do a lot of parsing and chopping of strings.
An XPath boolean value is either true or false, and boolean expressions, covered
earlier when you learned about conditional processing, evaluate to either true or false.
Location Paths
A location path is an expression that returns a node set. Location paths make XPath
powerful, but they also make the language complex. A location path describes one or
more nodes, where a node is the root node, an element, an attribute, a comment, a
namespace declaration, a processing instruction or some text.
A location path can be either relative or absolute. It is absolute if it begins with a
‘/’. As with absolute file paths and URLs, an absolute location path starts at the top
of the tree. A relative path starts with the context node. Unlike with URLs embedded
in Web pages, there is no rule of thumb that says that you should always use relative
URLs. In many cases, you should use them, but absolute paths can be invaluable in
grabbing data from distant parts of the tree.
Any location path is composed of location steps. Location steps can have three
parts—an axis, a node test, and a predicate. They take the following form:
axis::node-test[predicate]
This form probably looks unfamiliar to you, and you may be unconvinced that this
was the same language that you were using earlier. A couple of explanations are in
order. First, the predicate isn’t required. Second, the child axis is the default axis, which
means that a statement such as:
ROWSET/ROW
translates to:
child::ROWSET/child::ROW
Following this syntax, ROWSET and ROW are node tests. A node test is a node name, the
element wildcard (*), the attribute wildcard (@*), or a node-test function. If a node
name is used, only nodes of that name are selected. If the element wildcard character is
used, any element will match, while the attribute wildcard will match only attribute
nodes. Table 13.33 lists the available node-test functions.
346 Chapter 13
FUNCTION DESCRIPTION
Unlike with file systems, the wildcard can be used to skip levels of the tree. The fol-
lowing location paths select, regardless of who the parent is, any grandchild of the con-
text node named ENAME.
*/ENAME
child::*/child::ENAME
The last piece in the puzzle is the predicate. The predicate, which is optional, is a
way to filter down the node set achieved by the axis and the node test. Ultimately, it is
a boolean expression much like those discussed earlier. If the expression is true, the
node will be included in the resultant node set. There is one twist, however. If a num-
ber is given in the predicate itself, it will not follow the rules set out earlier. Rather, it
will evaluate to true for that particular node in the node set. So the following location
path:
ROWSET/ROW[3]
ROWSET/ROW[position()=3]
The position function is invoked on each ROW node, and only the third one will be
returned.
Axes
The XPath class is built around the concept of axes. You can think of an axis as a way
to define a relationship between two nodes. For any given two nodes and any given
axis, the two nodes are either both on the axis or not. Since XML describes trees, the
axes that XPath defines describe tree-based relationships. For instance, one popular
axis is the descendant axis of a particular node. Node B is on the descendant axis of
node A if it or any of its ancestors is a child of node A. Algorithmically, you can con-
sider the descendant axis this way:
XSLT In-Depth 347
self (.)
The self axis describes the reference element itself. It has (and always has) one mem-
ber only, the reference element. It can be abbreviated with the symbol “.” and is shown
in Figure 13.5.
348 Chapter 13
descendant-or-self (//)
The descendant-or-self axis includes the reference element and all of its descen-
dants. It always has at least one element, the reference element. It can be abbreviated
with the”//”symbol and is shown in Figure 13.6.
parent (..)
The parent axis includes, at most, one element, the parent of the context element. If the
context element is the root element, this axis will be empty; otherwise, the axis will have
one member only. It can be abbreviated with the symbol and is shown in Figure 13.7.
attribute (@)
The attribute axis includes the attributes of the given element. As discussed earlier,
attributes are considered nodes. This is the one XPath axis that works with nonelement
nodes. It can be abbreviated with the “@” symbol.
child
The child axis, which may be empty, includes all the immediate children of the con-
text element. It doesn’t include the context element, as shown in Figure 13.8.
descendant
The descendant axis, which may be empty, includes all the descendants of the con-
text element but does not include the context element itself. For elements without chil-
dren elements, this axis will be empty, as shown in Figure 13.9.
ancestor
The ancestor axis, which may be empty, includes all the ancestors of the context ele-
ment but does not include the context element itself. This axis is empty for the root ele-
ment and has at least one member for all other elements, as shown in Figure 13.10.
ancestor-or-self
The ancestor-or-self axis includes all of the ancestors of the context element and
the context element itself. It always has at least one member, the context element. All
elements except the root element will have at least two members on this axis, as shown
in Figure 13.11.
following-sibling
The following-sibling axis includes, at most, one element—the next sibling in
document order. If it exists, the only member of this axis is the next element with the
same parent. Figure 13.12 assumes that the elements on the left of the document pre-
cede the elements on the right.
preceding-sibling
The preceding-sibling axis includes, at most, one element—the previous sibling
in document order. If it exists, the only member of this axis is the first preceding ele-
ment with the same parent. Figure 13.13 assumes that the elements on the left of the
document precede the elements on the right.
following
The following axis, which might be empty, includes all elements that follow the context
element in the document. It does not include the context element, nor does it include
any ancestor of the context element.
preceding
The preceding axis which might be empty, includes all elements that precede the con-
text element in the document. It doesn’t include the context element.
namespace
The namespace axis includes all elements in the same namespace (as defined by
xmlns) as the context node. It will always include the context element, and it may
include nothing but the context element.
node-set current()
document
The document function returns one or more external documents as node sets.
format-number
The format-number function converts a number to a string in accordance with the
specified format.
ARGUMENT DESCRIPTION
ARGUMENT DESCRIPTION
generate-id
The generate-id function generates a unique identifier for a given node.
string generate-id(node_set_to_id)
string generate-id()
key
The key function is used in conjunction with an xsl:key element to look up values
according to the algorithm that the key element describes and using the second
parameter as input.
node-set key(key_name,node_set_to_evaluate)
node-set key(key_name,string_value)
node-set key(key_name,object_value)
ARGUMENT DESCRIPTION
ARGUMENT DESCRIPTION
As you will recall from the earlier discussion on the xsl:key element, keys are a way
of defining a search in XSLT. When you set up the xsl:key element, all you have to do
is use the key function to get a set of values that you want to pass in a parameter. The key
function uses the following algorithm to determine the nodes that should be included in
the result, assuming a string is passed as the second parameter of the key function.
1. The value of the match attribute and the value of the use attribute of the
specified xsl:key element are retrieved.
2. The node set specified by the match value is loaded from the XML source
document.
3. The first node in the node set is pulled.
4. The use attribute is evaluated against the first node to produce a value.
5. The value is compared to the string passed to the key function.
6. If the two are equal, the node will be included in the result node set.
7. The previous four steps are repeated for each node in the node set.
8. The result node set is returned.
In the case where a set of nodes is passed to the key element, the foregoing algorithm
is repeated for the value of each node in the node set. The result is a set of unique nodes
produced by all the evaluations. In the case where some other object is passed to the
key function, it is translated to a string as if by the string function and then the same
algorithm is returned.
system-property
The system-property asks the XSLT processor to return a system property.
object system-property(property_name)
ARGUMENT DESCRIPTION
There are only three properties that an XSLT processor is required to return:
xsl:vendor. The vendor that provides the XSLT processor.
xsl:vendor-url. The URL of the vendor.
xsl:version. The version of XSLT implemented by the XSLT processor.
A given XSLT processor may (or may not) return other properties.
unparsed-entity-uri
The unparsed-entity-uri returns an unparsed entity URI from the source docu-
ment type definition (DTD). If doesn’t exist, the empty string will be returned.
string unparsed-entity-uri(entity_name)
ARGUMENT DESCRIPTION
entity_name The name of the entity that has the URI you desire.
Node-Set Functions
count
The count function returns the number of nodes contained in a node set.
number count(node_set_to_count)
ARGUMENT DESCRIPTION
id
The id function selects an element by its unique XML id.
node-set id(string_list_of_ids)
node-set id(node_set_to_evaluate)
node-set id(object_to_evaluate)
Table 13.41 lists the parameters. The id for the nodes is specified by the source XML
DTD. If the DTD doesn’t exist or doesn’t specify a default, no nodes will be returned.
last
The last function returns the size of a node set. When used in a predicate, the size of
the predicate’s node set is returned. When used by itself, the size of the context
node-set is returned.
number last()
ARGUMENT DESCRIPTION
local-name
The local-name function returns the local name of a node—the name without any
namespace prefixes.
string local-name()
string local-name(node_set_to_evaluate)
If node_set_to_evaluate has more than one node, only the first node in
document order will be processed. If no argument is passed, the context node will be
evaluated.
Name
The name function returns the fully qualified name of a node, including any name-
space prefix.
If node_set_to_evaluate has more than one node, only the first node in document
order will be processed. If no argument is passed, the context node will be evaluated.
namespace-uri
The namespace-uri function returns the namespace URI of a node.
If node_set_to_evaluate has more than one node, only the first node in
document order will be processed. If no argument is passed, the context node will be
evaluated.
position
The position function returns the position of the context node within the current
context.
number position()
XSLT In-Depth 359
String Functions
concat
The concat function concatenates the argument strings.
contains
The contains function determines whether a string contains a substring.
normalize-space
The normalize-space function strips a string of leading and trailing whitespace and
replaces any sequences of whitespace characters with a single whitespace character.
string normalize-space(string_to_normalize)
string normalize-space()
starts-with
The starts-with function returns true if a string starts with a given string.
ARGUMENT DESCRIPTION
superstring The target string.
prefixstring The prefix string that the target string may or may
not start with.
360 Chapter 13
string
The string function converts an object to a string.
string string(node_set_to_convert)
string string(number_to_convert)
string string(boolean_to_convert)
string string(string_to_convert)
string string(object_to_convert)
string string()
string-length
The string-length function returns the number of characters in a string.
number string-length(target_string)
number string-length()
If a string is passed to the function, its length will be returned; otherwise, the string
value of the context node is returned.
ARGUMENT DESCRIPTION
Substring
The substring function returns a substring of a string.
substring-before
The substring-before function returns a substring of a string that precedes a given
token. It can be used to tokenize a string based on a delimiter.
ARGUMENT DESCRIPTION
ARGUMENT DESCRIPTION
substring-after
The substring-after function returns a substring of a string that follows a given
token. It can be used to tokenize a string based on a delimiter.
translate
The translate function translates the target_string by interchanging characters
of the base_string with characters of the source_string. The function can also be
used to remove characters from the target_string.
ARGUMENT DESCRIPTION
ARGUMENT DESCRIPTION
boolean Functions
The following functions return true and false. They have theoretical importance, but
probably the only one you will regularly use is the not function.
boolean
The boolean function returns a boolean value based on the argument
boolean boolean(node-set)
boolean boolean(string)
boolean boolean(number)
boolean boolean(boolean)
boolean boolean(object)
ARGUMENT DESCRIPTION
false
The false function returns false.
boolean false()
lang
The lang function determines whether a particular lang is the language or whether a
sublanguage specified by the argument is the same as the context node.
boolean lang(lang_string)
The lang value of the context node is determined by the xml:lang attribute of the
context node or the xml:lang attribute of the nearest ancestor that has an xml:lang
attribute. If no xml:lang attribute can be found, the function returns false.
not
The not function inverses the boolean value of its argument. True returns false and
false returns true.
boolean not(boolean_value)
true
The true function returns true.
boolean true()
Number Functions
ceiling
The ceiling function returns the next highest integer compared to the number
_argument or the number_argument itself if it is an integer.
number ceiling(number_argument)
floor
The floor function returns the next-lowest integer compared to the number_argument
or the number_argument itself if it is an integer.
XSLT In-Depth 365
number
The number function converts its argument to a number.
number number(node_set_as_single_node)
number number(string)
number number(number)
number number(boolean)
number number(object)
round
The round function rounds a number to the nearest integer or returns a number if it is
an integer, a positive infinity, a negative infinity, or a NotANumber.
number round(number)
sum
The sum function converts the string value of each node in a node set to a number, adds
them, and returns the result.
number sum(node-set)
ARGUMENT DESCRIPTION
Moving On
This chapter covered all the details of XSLT. At this point, you now have learned all the
core technologies that you need for developing XSQL applications. In the next chapter,
you’ll put XSLT, SQL, and XSQL to use when you develop a real-world application.
CHAPTER
14
Building XSQL Web
Applications
You’ve covered a lot of ground in the past few chapters. You’ve seen all of the syntax
and elements for XSQL, a large percentage of SQL and PL/SQL, and you’ve learned
XSLT. You know all the parts—now it is time to put them together. This chapter focuses
on using the knowledge that you’ve acquired to efficiently create XSQL applications.
The first step is to examine how to create an architecture of XSQL applications.
We’ve touched on this subject before, but this time you can focus on how to make spe-
cific architectural decisions, such as “Should I sort in SQL or in XSLT?” We’ll also dis-
cuss how Java action handlers come into play. Though you haven’t learned those at an
implementation level yet, it is important to understand the opportunities that they
make available to you. Often, action handlers can greatly simplify a task that would be
quite hard to accomplish in SQL and PL/SQL.
From here, you’ll walk through the process of designing and implementing a simple
application—an online product catalog for the Mom N’ Pup store. It has two parts: (1)
a public part that allows you to browse and search the catalog, and (2) a simple data
editor that allows you to input new entries. This example will be used to demonstrate
a process for designing an XSQL application architecture followed by the core code for
the public catalog part of the application. Based on this core code, you’ll extend the
application to include pagination, where large queries are separated into more man-
ageable pages. Then, you’ll develop the data editor, and that application will be used
to show how to integrate XSLT and JavaScript. The chapter ends by looking at how to
handle errors.
367
368 Chapter 14
Application Architecture
XSQL is more of a framework than a language. It allows you to easily leverage the
power of the database and XSLT. If your database is well suited to your application,
you can have a simple query-only application in place in a matter of minutes and hours
instead of days and weeks. For applications that are more complex, you can extend the
basic model with action handlers and PL/SQL procedures. Even then, you’ll still hope-
fully find yourself writing far less code than you would in a traditional n-tier develop-
ment model.
“Hopefully” is the key word here. The chief challenge of XSQL development isn’t to
figure out how to manipulate strings or write the most efficient loops in XSQL—you
can’t do those things in XSQL anyway! Rather, it is to make the best decisions regard-
ing where to put the logic. This section looks in detail at this question. Because each of
the individual technologies is so powerful, a lot of the decisions are trade-offs. For
instance, both XPath and SQL are searching languages—when should you use each?
Should you extend the framework with stored procedures or Java action handlers?
Your first step is to examine the simple XSQL model that uses only SQL and XSLT.
From there you’ll look at how to consider how stored procedures and action handlers
fit in. Then, we’ll talk about the various ways to extend the simple model. Before
launching into the various architectures, you may wish to review the high-level
architecture figures earlier in the book that describe the various components. In this
section, you’ll see how to think about the different components in terms of application
development.
The real challenge facing us: Which component should do what? There are no
absolute answers, but the next few pages should give you some guidelines for exam-
ining this question. The way you answer this question will have a lot to do with your
preferences and the preferences of your team and management. For instance, if you’re
a Java guru, you may find that action handlers are the primary building blocks for your
solutions. If your management prefers to have everything in PL/SQL, then you’ll find
that many of the action handler duties can be handled in PL/SQL stored procedures.
Perhaps the best way to approach this section is by focusing on the parts that you don’t
know so well. You may find that you can more efficiently handle a task with PL/SQL
in spite of being a Java master or that you can win over your team of Java action han-
dlers and change the policy.
Building XSQL Web Applications 369
■■ You can have more than one xsql:query action per page.
■■ You have access to all of the built-in SQL functions and PL/SQL functions.
■■ You can use the searching capabilities of Oracle Text inside SQL queries.
■■ You can use cursors inside SELECT statements to provide a deeper result set
of data.
All of these combine to give your xsql:query actions a lot of power. Your
stylesheets don’t care how many xsql:query actions you have in a page—they just
translate the XML. There are lots of SQL functions and Oracle-provided PL/SQL func-
tions that can help you process the data in the database and provide better data in the
output. Perhaps most important, you can use cursors inside a SQL statement so that
the result is multileveled. These are especially useful to XSLT stylesheets.
You can’t do everything with a SELECT statement or two, but you can do a lot. Two
obvious cases in which you need to move beyond the xsql:query action are (1) when
370 Chapter 14
you need to perform intermediate queries in which one query dictates how another
should be formed, and (2) when you need to return XML from the database into your
document. There are certainly others. For example, you may find that it is easier to
maintain an action handler or stored procedure that makes multiple SQL queries, even
though the result could be produced with a single, very complex SELECT statement.
Let’s turn our attention to the Data Manipulation Language (DML) statements. As
mentioned in Chapter 7, “Database Modifications with XSQL,” the xsql:dml action
actually contains an anonymous PL/SQL block. This means that you can put any num-
ber of statements in the action and can even use conditional processing and variables.
The real drawback with the xsql:dml action is that you can’t provide good feedback
to the user. You can put data in, but you can really only communicate back to the user
whether it was successful. Often, you want more granularity than that. You can place
an xsql:query statement after the xsql:dml statement, but this doesn’t give you a
lot of control. The xsql:dml action runs out of runway when you wish to provide pre-
cise feedback regarding what happened while you were modifying the data.
These distinctions may seem hopelessly abstract at this point. As you work through
this chapter and develop a sample application, they should start to make more sense.
In advance of that, the next section outlines a design process that can help you to detect
when you need to move beyond the simple architecture.
the wrong path. Let’s look at the process first, and then we’ll examine how to tell if you
need to move beyond the basic architecture.
1. Develop the requirements for the system, and/or pester the appropriate person
to actually write the requirements down.
2. Develop a site map of the interface and possibly a mockup.
3. Identify the places where you need to retrieve dynamic data and user input.
4. Identify the database tables, views, and other objects that relate to each item
found in step 3, or design the database if one doesn’t exist.
5. Create the SQL SELECT statements that you need for the queries.
6. Create the DML statements that you need to input data.
7. Identify when parameters are going to need to be passed from one page to
another and the best way for doing that (e.g., cookies, page parameters).
8. Create XSQL pages for each page in your site map, encapsulating the SQL from
steps 5 and 6 in XSQL actions.
9. Tie the XSQL pages together so that parameters are passed appropriately.
10. Develop the core XSLT stylesheets using live data from the database.
11. Develop the error handling stylesheets.
In 11 fairly easy steps, you’ve created your Web application! This does assume, of
course that your problems could be solved with the simple architecture. However, you
should be able to determine if the simple architecture will suffice on or before step 6
and before you do any real work. Let’s look at how to tell if the application isn’t going
to fit the simple architecture and the steps to get around the problem:
1. Develop the requirements for the system, and/or pester the appropriate person to actu-
ally write down the requirements. With any luck, you’ll be able to get past this
step. Unfortunately, many projects fail because the basic design of the system is
never formalized. Some would argue that success here requires a full-blown
functional specification and technical specification with lots of diagrams. In
reality, there certainly are projects that are small and simple enough in scope
not to require such formalism. Even then, however, the process of writing
down a couple of paragraphs can reveal certain types of problems early on. It’s
also helpful when the project goes sideways and everyone wants to claim that
such and such a feature was required from the very beginning. Of course,
XSQL doesn’t require such a document, but in spite of this, its power shouldn’t
be underestimated.
2. Develop a site map of the interface and possibly a mockup. Your first red flag may
appear during this step. If your application requires user authentication, then
the simple XSQL architecture may not work for you. You may be able to handle
this at the Web server or authentication level. For instance, if the Web server
won’t open a URL except for authenticated users, you can use the simple XSQL
372 Chapter 14
architecture for the pages themselves. However, if you wish to implement com-
plex rules for your users, you may find that you need an action handler to take
care of this.
3. Identify the places where you need to retrieve dynamic data and user input. This step,
in and of itself, shouldn’t present any problems. It is important to complete this
step without thinking too hard about the database, XSQL, and other implemen-
tation issues. The remaining steps will flesh out the specifics of your applica-
tion’s architecture.
4. Identify the database tables, views, and other objects that relate to each item found in
step 3, or design the database if one doesn’t exist. First, if you are creating the data-
base as part of your development effort, then you’ll need to design the data-
base now. A section further in this chapter covers this topic extensively. If this
application is going to be built on top of a database that already exists, then
you’ll need to acquire documentation on the underlying database. After discov-
ering that no one has documented the database, you should needle the data-
base administrator (DBA) to tell you which tables you’ll need and how they
relate to each other. If you plan on putting data into the database, then you’ll
want to be very sure that you aren’t going to corrupt the data or interfere with
other applications that use the database.
Whether the database exists or not, an obvious problem may crop up here.
What if your application gets data from places other than the database, or what
if it gets data from multiple databases? If you are getting data from a nondata-
base data source, such as a Lightweight Directory Access Protocol (LDAP)
server, then XSQL may not be useful to you. In such a case, your first step is to
investigate whether you can get your data as XML by calling a URI. If you can,
you might be able to use the xsql:include-xml action inside your XSQL
pages. You simply hand it a URI, and the XML that is retrieved is placed into
the XSQL output. Then your stylesheet just needs to be able to transform it
appropriately.
If you find that your nondatabase data source won’t return good XML to you,
you’ll probably need to create an action handler. Inside the action handler, you
can reach out to the data source by whatever means necessary. If the data
source is an operating system file, you can read it. If the data source is reached
by a URI or by some other kind of network connection, you can write code to
reach it over the network. If you need to reach the resource through a C Appli-
cation Programmer’s Interface (API), you can use the Java Native Interface
(JNI). As long as you can reach the data source through Java code and you can
get the data quickly, you’ll be able to reach it from the action handler. Also, if
the data that you are trying to reach doesn’t change much over time, you might
find it easier just to create a table in your database for it. Then you can grab the
data with a simple XSQL query. Instead of creating an action handler, you just
need to create a script to load the data as needed into your table.
What if you have more than one database that you need to access? First, it
doesn’t matter if the databases are Oracle or not—they just have to be reachable
by Java Database Connectivity (JDBC). The problem is that any given XSQL
page can only connect to one particular database. There are two ways to
Building XSQL Web Applications 373
address this. First, you can use the xsql:include-xsql action to include the
results from another XSQL page. Your main page can attach to one database
while the page or pages you specify in each xsql:include-xsql action can
attach to other databases.
It is also possible to link the databases together at the database level. This is
especially easy if all of the databases are Oracle. You create a distributed data-
base that is composed of several databases but appears to your SQL statements
to be one.
5. Create the SQL SELECT statements that you need to create the dynamic data
parts. For this step, your goal is to make sure that you can construct SQL
SELECT statements to grab the different dynamic parts that you’ve con-
structed. Remember that you can embed parameter and cookie values into
your SELECT statements. You should watch out for cases in which you want to
perform completely different SQL statements based on different parameters or
where you actually need to execute multiple SQL statements to get the desired
result. In either of these cases, an action handler or a stored procedure is proba-
bly in order.
If you need to execute multiple SQL statements, a stored procedure will be
more efficient. Before going that route, however, you may want to investigate a
little further and make sure that multiple SQL statements are really necessary.
Often, you can accomplish what you need to do with a single SQL statement by
using a complex where clause, aggregate functions, cursors, and unions. Writ-
ing good SQL for XSQL is covered later in this chapter.
Though it is more efficient to handle multiple SQL statements inside a stored
procedure, an action handler can make a great traffic cop. If you have several
complex forms tied together and each step depends on a previous step, then
you can use an action handler to decide on the correct next action by looking at
the current parameters and a possible user session.
When it looks like your problems won’t be solved with simple SQL statements,
there are no hard and fast rules about whether you should use an action han-
dler or a stored procedure. In most cases, the answer is application dependent.
At this point, it is best to make a note of those situations that are more complex.
Before making any decisions, you should learn more about writing action han-
dlers by reading Chapter 18, “Custom Action Handlers,” and review the
stored-procedure discussions in Chapter 9. From there, you should look for
similarities in the different cases. It’s quite likely that you’ll be able to solve
multiple problems with a single stored procedure or action handler.
6. Create the DML statements that you need to input data. The simple architecture
will be adequate if you have a couple of values that need to go straight into the
database without any processing and you only need to know that the transac-
tion succeeded. However, if you need to base what is inputted on variables in
the form, you’ll need an action handler. For example, if a radio button setting
determines how the data should be interpreted, you should use an action han-
dler to do the interpretation. You may find a stored procedure helpful, but an
action handler affords a lot more flexibility.
374 Chapter 14
7. Identify which pages need to be tied together and whether you need parameters to pass
from one page to another or you need cookie-based sessions. XSQL supports the basic
methods of passing parameters used by HTTP-based applications. You can set
cookies on the browser, and you can process and pass GET and POST parame-
ters. Thus, you can handle forms, parameters passed in the query string, and
the getting and setting of cookies. In the simple architecture, you can pass any
parameter and cookie value to the xsql:query and xsql:dml actions. How-
ever, if you want to do a complex interaction (e.g., performing one query based
on one parameter value but a different based on another parameter value),
you’ll probably want to consider writing an action handler or creating a stored
procedure for that purpose.
Cookies deserve special consideration. Cookies are usually used as a way to
bind different HTTP transactions together into a session. Because the cookie
value returns on subsequent calls to the server, you can assume that the same
user (or at least the same Web browser) is reconnecting. The cookie may con-
tain meaningful data, or it may just contain a pointer to data that is stored on
the server.
The latter architecture tends to be more flexible—only one cookie needs to be
set. In fact, Java servlets use this architecture so that you never have to set a
cookie at all. From an action handler, you can use this servlet session architec-
ture. Instead of setting and managing cookies directly, the servlet container
takes care of the plumbing. Because the database is readily accessible, you can
even store the session data in the database and reapply it to a session when the
user returns, as discussed in Chapter 6 (“XSQL Parameters”). You can also
assign and read cookies from inside an XSQL page. Cookies can play a role in a
simple XSQL architecture. With some clever SQL statements and maybe a
stored procedure or two, you can have something that looks a lot like the ses-
sion object available via an action handler. The question is: Is that worth the
effort? If you find that your application needs session capabilities, you’ll find it
easier to use an action handler to handle them.
8. Create the XSQL pages, encapsulating the SQL from steps 6 and 7 in XSQL actions.
This doesn’t necessarily result in a single XSQL page for each page of the site
map. If you have a dynamic data area that is repeated throughout the site, you
can create a single XSQL page for it and include it in other XSQL pages.
9. Develop the core XSLT stylesheets using live data from the database. Now that the
XSQL is developed, you can write your XSLT that produces the presentation
layer. As with the XSQL pages, you should be on the lookout for code reuse.
Rather than developing the same stylesheet several times, you can develop it
once and either include or import it from other stylesheets. You’ll see an exam-
ple of this later in this chapter.
You’ll also need to consider the roles of cascading stylesheets (CSS) and
JavaScript in your Web site. When used with the Web, an XSLT transformation
creates HTML for consumption by a Web browser. If you wish, the provided
HTML can contain CSS and JavaScript. There is nothing inherent in XSLT that
prohibits the use of JavaScript or CSS in any way. CSS and JavaScript is just
Building XSQL Web Applications 375
text, after all. For instance, you could even use XSLT to dynamically create CSS
styles and JavaScript code.
Generally, however, you want your CSS styles and JavaScript code to be
static—debugging can be quite hard if the code itself is generated on the fly!
Your CSS styles should be separated into a separate file for use by the entire
site. You should try to do this as possible for JavaScript functions, too, though it
can be harder for JavaScript. Because many functions are specific to particular
forms and page elements, it’s fine to keep those close to the elements with
which they deal.
The real issues with CSS and JavaScript involve how styles and functions
should be referenced and invoked from stylesheet templates. This subject is
covered in detail later in this chapter.
10. Tie the XSQL pages together so that parameters are passed appropriately. At this
point, you may run into some of the same difficulties described in step 5. You
may discover that you are better off using a servlet session instead of handling
all of the parameters manually.
11. Develop the error handling stylesheets. Your last step is to develop the stylesheets
that handle errors. You’ll learn more about this later in this chapter. It is basi-
cally a two-step process: (1) Determine what errors could occur; (2) determine
how you want to inform the user about them. It is also helpful to develop a
generic error handling template that simply displays a message.
Through this process, you’ve seen that there are many problems that the simple
XSQL architecture can’t solve. However, as you’ll see throughout the rest of the book,
the problems can be solved with a variety of different strategies. The art of XSQL devel-
opment is choosing how to extend XSQL. The choice of how to extend is addressed
specifically further in the next section and will enter into our discussions for the rest of
the book.
Extension Options
In the previous section, you saw how to develop the architecture of your application.
There are many times when you need to extend beyond the simple architecture. There
are four ways to extend XSQL: (1) Write an action handler; (2) write a stored procedure;
(3) invoke XSQL programmatically; and (4) write an XSLT extension. You’ve already
learned about stored procedures in Chapter 9. This section looks at how to view each of
these options in terms of the overall design of your application. You’ll start with stored
procedures, and then continue into the subjects that you haven’t covered in depth.
Stored Procedures
You learned a lot about PL/SQL stored procedures in Chapter 9. This discussion cen-
ters not on the mechanics but on the concepts. Instead of worrying with how you
would implement a particular functionality with a stored procedure, the question here
is what you can do with a stored procedure and if it is best to use a stored procedure
376 Chapter 14
for a particular purpose. For this discussion, it doesn’t matter if you are using PL/SQL
for your stored procedures or Java.
There are two key advantages of stored procedures: you can integrate very easily
with SQL and your code runs inside the database. As is so often the case, the latter
advantage can also be a disadvantage, depending on your system configuration. Like-
wise, the tight integration can mean that you have limited access to other system
resources beyond the database.
First, let’s look at the advantages. With PL/SQL, SQL is built directly into the lan-
guage. The results of SQL statements can be stored in variables and processed further.
With Java stored procedures, you use SQLJ to accomplish the same goal. This tight
integration makes it far easier to develop database-intensive code than using JDBC
from an action handler.
The other key advantage of stored procedures is that they are executed inside of the
database. This means that you don’t suffer network round trips. When you call SQL
from an action handler or other client program, each SQL query means a trip across the
network and the return of a possibly large data set. Stored procedures aren’t necessar-
ily the solution to all of your performance worries, but you will at least be able to save
some load on your network and the requisite time required in transferring data.
However, stored procedures are limited in dealing with resources beyond the data-
base. If you need to deal with nondatabase data, such as files or other servers, then
stored procedures won’t be much help. This underlies the key point about stored
procedures: They are extremely special-purpose.
Perhaps the best way to think about stored procedures is as extensions to SQL. If
you find yourself needing data from your database and SQL is too simple for your
task, a stored procedure is a natural alternative. You’ll find that they are easy to write
and use. However, if you find yourself doing more typical, less database-intensive
tasks, you should probably be using action handlers.
Action Handlers
Action handlers are the key to the programming unit for the entire XSQL framework.
Because you haven’t actually created an action handler yet, they may be hard to grasp.
The easiest way to think about them is to remember that all of the actions you’ve been
using, such as xsql:query and xsql:dml, use action handlers. When you write
your own action handlers, you can solve the same class of problems solved by the
built-in actions that you’ve been using all along. You can query the database, put data
into the database, run stored subprograms or most anything else.
Most important, you can invoke the built-in action handlers in your action handlers.
If you want to do multiple SQL calls, just call the xsql:query action handlers
Building XSQL Web Applications 377
multiple times. If you want to input data, do a query, and then input more data, you
can do that with very little new code.
The built-in action handlers can take values and attributes of the invoking element
as inputs. Your action handlers can do this, also. Additionally, you can also access the
data that came with the HTTP request. Your action handler is responsible for passing
an XML element back to the XSQL servlet, which inserts it into the output.
With an action handler, you can do anything that you’d like to do with a stored
subprogram. As covered earlier, there are advantages to coding the most database-
intensive parts of your application as storedsubprograms. The stored subprograms
that you create can be used in conjunction with any action handler made. Action han-
dlers and stored subprograms are in no way mutually exclusive.
In Chapter 18, you’ll implement your own action handlers. If you’ve written servlets
or CGI programs, you’ll probably be impressed by the simplicity and power that action
handlers can give you. The details of the underlying HTTP protocol are available, but
you don’t have to manage everything about the transaction. You can easily query the
database but still do anything else that a servlet can do. You can also pare down or aug-
ment the XML before returning it. As you work through this chapter, you may find the
XSQL built-in actions to be limiting. As you’ll see, though, action handlers can over-
come these limitations and then some.
Programmatic Invocation
So far, the XSQL page has functioned as the key to the entire application. It drives the
action. But you can also invoke an XSQL page from inside of an application written in
Java. You create an instance of the XSQL page processor, and it loads a specified XSQL
page. If you’ve defined action handlers and serializers in the XSQL page, it invokes
those. If your XSQL page needs parameters, you can set the parameters before page
processing. When the page processing is done, you can do whatever you wish with the
output. You could even interpret it as XSQL and pass it back to the XSQL page proces-
sor for more processing.
There are several advantages of programmatic invocations. First and foremost, you
have complete control. You can decide, at runtime, the XSQL page that you wish to use.
You could even create the XSQL page at runtime if you’d like. When the page proces-
sor completes its job with the XSQL page, you can do what you wish with the results.
If you are invoking the XSQL page processor from inside a servlet, you can modify the
results before sending them onto the Web. Or, you can do something else with the
results entirely.
The programmatic API gives you all the power you need to do anything that you
want with XSQL. You aren’t limited to just Web applications or the input-output model
that you’ve been using thus far. Instead, you can think of programmatic invocation as
a general way to fetch and input data with the SQL defined externally. As a bonus, you
get the results of SQL back as XML. You can even perform XSLT transformations inside
your programs if you want.
Power always comes with a price, though. The danger with using the programmatic
APIs is that your overall architectures can become muddled. A chief advantage of a
framework is that everyone knows where all the moving pieces are. It’s easier to bring
new developers onto a project because the architecture itself is standardized. They
378 Chapter 14
don’t have to relearn what all the pieces are and how they fit together. Of course, not
all problems fit nicely into a framework. Programmatic invocation allows you to mold
the framework any which way that you desire. However, if you don’t carefully con-
sider how best to use the programmatic APIs, you can end up losing a lot of the bene-
fits of the framework itself. A minimalist approach is often beneficial. In Chapter 17,
“XSQL Beyond Web Browsing,” you’ll see the details of programmatic invocation, and
you’ll see how to use it to augment the basic XSQL architecture.
Stylesheet Extensions
Our last topic is stylesheet extensions. The Oracle XSLT processor allows you to call
Java methods from inside a stylesheet. You do this by declaring the Java class that you
wish to use and then calling the method inside a value-of element. The most palatable
uses involve simple conversions using static methods. For instance, you could change
a number into a binary number.
There are a number of reasons why stylesheet extensions are bad and are best
avoided. First, they haven’t been formally standardized. If you ever find that you wish
to use a different XSLT processor with your stylesheets, they may not work. Also, they
tend to make your stylesheets harder to understand. At the very least, someone look-
ing at your stylesheets will need to defer to the documentation of the method just to
understand what you are trying to do. If you develop a stylesheet extension habit, your
stylesheets can start to look more like scripts instead of templates.
The problems that extensions solve can always be solved elsewhere in your archi-
tecture. A stylesheet extension takes as input a particular piece of data from the input
XML document. The most that it can reasonably do is modify that string. Earlier in this
section, we used the example of changing a decimal number to a binary number.
Another example is rewriting the value as uppercase. Sometimes, these are necessary
manipulations. However, you can always do them in an action handler before the XML
reaches the XSLT processor.
reserved XML characters when creating JavaScript on the fly. JavaScript also intro-
duces another conceptual issue: How do you make methods that can be called from
inside templates that may be applied numerous times?
Let’s consider CSS, as JavaScript is covered in detail later in this chapter. Your CSS
rules should be in a separate file for use by the entire site. This is a generally accepted
best practice whether XSLT is used or not. It’s especially important when used with
XSLT. Stylesheet bugs tend to occur because templates aren’t applying properly to the
XML elements in the input document. If you are creating CSS rules inside a particular
stylesheet, you are adding yet another moving part to your code. It’s far better to
define the CSS rules in a separate CSS file. Then only the selectors reside inside a
stylesheet template.
This approach assumes that you aren’t dynamically generating CSS rules. What if
you store a user’s preferred colors in the database? Even in such a case, there is still a
strong argument for setting up the set of possible CSS rules in the CSS file and then
using XSLT to dynamically select the right style. However, if the best option really is
dynamic rule generation, you certainly can do that with XSLT. You can either dynami-
cally generate the CSS file itself using XSQL, or you can generate the rules inside the
templates of an XSLT stylesheet.
A Sample Application
Our sample application is an online catalog for the Mom N’ Pup retailer. In the course
of developing this application, we’ll run into some issues that are best resolved with
action handlers and a programmatic approach. For now, we’ll follow roughly the
design process that we laid out in the previous section. Much of the remainder of our
discussion in this chapter will revolve around our development. In this section, we’ll
develop the site map for the application and implement the database schema.
The Requirements
The first step in our process is to state the requirements for the application. The cus-
tomer wants an online catalog that will be available over the Web. They currently don’t
have a database, so they’ll need to have one built to contain the data. They want the
users to either browse based on product category or perform searches based on the
descriptions. In addition, they want to be able to edit the prices of products through a
Web interface. At a high level, the requirements are as follows:
■■ Web-based online catalog, browsable by product category and searchable by
keywords
■■ Database to store data
■■ Edit function for prices
In addition to these requirements, they are also throwing a couple of curve balls.
First, they want some products to belong to more than one product category. For
instance, hand soap could be used in the kitchen or in the bathroom. They want hand
soap products to appear under both kitchen supplies and bathroom supplies, though
for internal purposes it should be stored under only under one product category.
They’ve browsed some other product sites and don’t like it when too many product
titles are loaded at the same time. Instead, they want the results of a query to be sepa-
rated across several pages.
Mom N’ Pup also runs specials from time to time. They want the home page to have
a list of specials on the right-hand side of the home page. Clicking on the ad will take
the shopper directly to the product page for the particular product. The specials should
also be configurable through a Web-based interface.
The customer is also aware that the Web is an evolving medium, and they want to
be sure that their catalog will be usable via different types of interfaces. They require
that a demo version of the catalog is available through Wireless Application Protocol
(WAP) that allows browsing of products only. They figure if the application design
allows for this, then they’ll be ready for any type of interface. This leaves us with our
secondary requirements:
■■ Products can appear as members of multiple product categories.
■■ Product listings should paginate.
■■ Ads for specials should be listed.
■■ Specials should be configurable through a Web interface.
Building XSQL Web Applications 381
Now at this point, the imaginary customer would sign an imaginary contract drawn
up by imaginary lawyers. Luckily for us, this step isn’t necessary. If this were a real
project, we’d also need to examine issues such as where the site is going to be hosted,
how the data is going to be loaded, and how the database is going to be backed up and,
in case of a failure, restored. These issues are largely beyond the scope of this book, so
we’ll punt those. Instead, we’ll begin with the development work starting with the
user interface design.
With this as a starting point, the rest of the site can be formulated. The primary goal
of the site is to drive people to product-specific pages. Each product needs to have its
own page with descriptions and pricing information. It can be reached through one or
more product category pages, a search result page, or the ad. The primary site naviga-
tion is diagrammed in Figure 14.2.
If you stopped here, you would have a functional application, but not that pleasing
of a user experience. Once users start to drill down, they wouldn’t have any way to
navigate back to the home page or across the site. Users need some sort of navigational
elements to help them move around the site. The proposed resolution is to have a nav-
igational tool bar across the top and a listing of product categories along the left-hand
side. These navigational elements will appear in all of the subordinate pages—the
product pages, the search result pages, and the product category pages. A mockup of a
product page is diagrammed in Figure 14.3.
We have two mockups left to complete—the search results page and the product cat-
egory page. Both are very similar in nature—they list products. The search results page
is simpler in nature and is displayed in Figure 14.4. It lists the products based on their
names, and the links lead to the respective product pages.
Home Page
Product Page
Our last page to design is the product category page. The main difference is that all
of these products are related by product category, and the product category name and
description should appear at the top of the page. It is pictured in Figure 14.5.
In these last two examples, you’ll notice that there are Previous and Next buttons at
the bottom of the pages. These meet requirements for pagination of the results. In the
mockup, Previous is grayed out because we are presumably at the beginning of the
search results. If we are at the end, the Next link should be grayed out. If a particular
search doesn’t return enough results to require pagination, neither should appear.
This takes care of the public catalog. Now, attention is needed for the price editor
and the promotion editor. The customer wants to be able to change the prices of the
items easily and to change multiple items at the same time. The editor page itself can
appear similar to Figure 14.6. The top search field allows users to search for the prod-
ucts that they wish to edit.
At this point in the process, we would ask our customer to review our mockups in
order to ensure that we are heading in the right direction. This is another advantage of
starting with the interface—your customer can see it and understand it. An E-R dia-
gram isn’t nearly as exciting to most customers as a couple of Web pages tied together
that kind of look like they work. This is also the time when the requirements can be
clarified and possibly expanded. (Just make sure that if the project expands, the price
also expands.)
In our case, Mom N’ Pup is delighted with our work and are already hailing us as
geniuses! (Hey, we might as well make the most out of our imaginary customers. They
are much more malleable than the real ones.)
Database Requirements
At this point, you’ve moved through steps 1 and 2 in our process. You have the require-
ments and you’ve developed an interface. The next step is to formulate the database
requirements. To do this, you need to examine the interface design and identify
the areas of your interface that interact with your database. With the areas identified,
you can come up with pseudo-SQL statements that you will need for the required
functionality.
First, a word is needed about our pseudo-SQL. This isn’t yet another new-fangled
language you have to learn. It’s just a way to describe the data that is needed from the
database in plain English. Because we don’t have a database yet, we don’t have to be
syntactically correct. Even if we did have a database, this method is a good way to
quickly determine the requirements without getting mired in the workings of the
actual database. For our pseudo-SQL, we deliberately leave the tables out of the pic-
ture for now and focus on the pieces of data that are needed. Determining which fields
should live in which tables is a task that will be addressed in the database design
phase.
To find these database-related areas, we need only to examine the mockups formu-
lated in the previous section. Let’s start with the home page described in Figure 14.1.
There are two dynamic data areas: (1) the listing of product categories and (2) the pro-
motions. Our interface design puts usability limits on the number of each of these, but
386 Chapter 14
they can’t be static. They should be generated from the database. Here’s a pseudo-SQL
statement for the product categories:
Our statement retrieves the data that is seen on the screen as well as the product cat-
egory identifier. The product category identifier will be embedded in the link. This
illustrates an important point of this exercise. You need to look not only at what is dis-
played in the mockups, but also at data points that are needed for behind-the-scenes
work. The next pseudo-SQL statement also illustrates this point:
There is one database-related area left on the home page: the search field. It is actu-
ally a simple static form, but it will need to be linked to an XSQL page that can handle
the form. For talking purposes, let’s name the form handler textSearch.xsql.
The textSearch.xsql page will need to have its own SQL statement based on the
following:
When the Search button is pressed, the search results page will be displayed. The
dynamic data area for that page is furnished with the preceding SQL statement. When
users click on a product, they need to be taken to the product page. The product ID can
be embedded in the link to handle this. The link will be to product_details.xsql,
and this XSQL page should contain a query like this:
This covers our search functionality, and this same query can be used when users
click on a promotion and when they select a product from the product category page.
Now, let’s back up and cover the browsing functionality. Our product category names
will be linked to the product category pages. As with the search results, we can assume
Building XSQL Web Applications 387
that the type_id for a product category will be embedded in the links that will be
generated for the home page. They’ll be handled by an XSQL page that you will call
product_type.xsql. It needs a SQL statement to select the products. It should
look something like this:
The queries that have been developed thus far cover nearly all of the primary func-
tionality of the application. Now we have to flesh out the navigational aspects. The
first step is the listing of product categories that appear on the left side of the subordi-
nate pages. This one is similar to our earlier query for the home page, except we need
less information:
We also have a horizontal navigational element across the top of the page. It con-
tains a text search field along with a link to the home page and the product category.
The home page link is always the same. The category link is a bit trickier. We could
write a query that pulls the primary category for the displayed product. Remember,
though, that one of our requirements is to be able to assign multiple categories to a
product. The product category that is returned by the query may not be the category
that the user used to navigate to this page. Thus, we shouldn’t formulate a query for
this information. Rather, we need to pass the category ID as a parameter.
Our analysis for the public part of the application is now complete. The price editor
part of the application is covered separately later in the chapter.
Database Design
In the previous section, we identified all of the areas in our application that should
interact with the database. We have a good idea of the kind of database that is needed.
Now the trick is to put the database together. Before plunging in, the requirements
should be revisited. The customer will be providing XML for the product descriptions,
and we’ve determined that it makes the most sense to store the XML documents in the
database. We also know that there is a many-to-many relationship between the prod-
uct categories and the products. A single product can belong to multiple product cate-
gories, and of course, a single product category can have multiple products.
With these requirements in mind, we can consider the key question in database
design: Should the database be normalized or denormalized? Per our earlier discus-
sion, you know that denormalization is best reserved for data warehousing-type appli-
cations. If this were an extremely large product catalog, then a denormalized database
388 Chapter 14
may be necessary. However, once you go the route of optimizing for performance, you
risk making a database that is hard to extend for other applications that might come
along. Because our product catalog isn’t going to contain hundreds of thousands of
items, the normalization approach is best. Besides, it’s far easier to make a denormal-
ized database based on a normalized one than to try to go the other way.
Now it’s time to look back at the pseudo-SQL developed earlier and determine the
necessary fields. Each field should be grouped with an entity.
PRODUCT
Product identifier
Product price
Product name
Product summary
Primary product category
Complete product description
PRODUCT CATEGORY
Product category identifier
Product category name
Product category description
PROMOTION
Promotion slot
Product ID for the promotion
URL of the promotional image
Promotion status
The next step is to make sure that our entities are in the third normal form, described
earlier in this chapter. You can determine this by stepping through first, second, and
third normal forms. The first normal form says that there shouldn’t be multiple
columns for the same field. This doesn’t look to be a problem for any of our entities. If
the product category had columns for each product in the category, we would be in
violation of the first normal form. Usually, developers are instinctively in compliance
with first normal form. However, if you find yourself putting a comma-delimited list
in a column, then you are in violation of the first normal form. You should take what-
ever data that you are listing and make a table for it.
Next, we need to see if we are in compliance with the second normal form. The rule
for the second normal form states that you need to have a primary key, and the nonkey
Building XSQL Web Applications 389
fields need to be dependent on it. The product and the product category entities meet
this test—you can use the product identifier and product category identifier as the pri-
mary keys. However, the promotion entity isn’t as straightforward. You could use
either promotion slot or product id as a primary key, but this is a bit limiting. What if
you want to tie multiple promotions to a single product? What if you want to tie mul-
tiple promotions to a particular slot and rotate them? It is best to modify the promotion
entity so that it has a promotion ID that is independent of the other entities:
PROMOTION
Promotion ID
Promotion slot
Product ID for the promotion
URL of the promotional image
Promotion status
Last, you need to check to make sure that all of the entities are in third normal form.
The question is: Are there any nonkey columns dependent on other nonkey columns?
There are not. If we had combined the product and the product category tables, then
there would be a problem. The product category name and description would be
dependent on each other. The purpose of third normal form is to make sure that tables
shouldn’t be split up.
Now that the database is normalized, it’s time to set up the relationships between
the different entities. There are three relationships: (1) promotion to product, (2) prod-
uct to primary product category, and (3) multiple product categories to product. The
promotion-to-product relationship is easiest. Per the requirements, it is a one-to-one
relationship, but there is no harm in making it a many-to-one relationship. This yields
the ERD for these two entities, as shown in Figure 14.7.
In terms of our fields, the product ID for the promotion field in the promotion entity
can be tied to the product identifier field in the product entity. When you implement
the database, you can create a foreign key constraint between these two columns.
The primary product category to product relationship looks basically the same.
Every product needs to have a product category to which it belongs. The diagram for
this relationship is shown in Figure 14.8.
Promotion Product
Product
Product
Category
Our last relationship is more complex. For browsing purposes, the customer wants
some products to appear in multiple product categories. This will make some products
easier to find. At the database level, though, it means that we need to define a many-
to-many relationship between the product table and the product category table. Draw-
ing the ERD is easy—it is shown in Figure 14.9. However, actually implementing the
relationship isn’t as simple.
The problem is that you need a list of the different product categories to which a
product should belong. However, as you know from the normalization discussion, you
can’t store the list in the product entity. To do so violates the first normal form, and the
list would be hard to maintain. Likewise, you can’t store a list of all of the products for
a product category in the product category entity. The best solution is to create a join
entity that resolves the many-to-many relationship:
Product
Product
Category
Product
Product Join Table
Category
Promotion
Database Implementation
Now you get to write your first real code. In this section the design starts to become
reality. You’ll create the four tables along with their constraints. You’ll also create three
sequences for use by the tables. Any time you create a database, it is a good idea to put
all the commands into a script. If you ever have to recreate the database, then you can
just rerun the script.
The first step is choosing the order in which the tables should be created. When cre-
ating a database, it’s best to first create the tables that don’t define foreign key con-
straints. Then when you create the tables that do define foreign key constraints, the
tables they reference already exist. This keeps you from having to define foreign key
constraints separately. By examining the ERD, the order of table creation should be:
1. product_category
2. product
3. promotion
4. product_category_joiner
The creation of these tables is as follows:
The tables are created. Now you need to create sequences for the product_cate-
gory, product, and promotion tables. These sequences will be used to generate the
next primary key for these tables:
The final step is to create an index for the doc column of the product table. This step
is best performed after data is loaded into the table. It’s also important to remember to
reindex the table after new data is added. The command for creating the index is:
Now your database is complete. There’s no need to waste the space here loading the
database. You can download the scripts for that from the Web site described in the
introduction.
The next SQL statement to write is for the text search capability. You’ll use the
Oracle text that you learned about in Chapter 8 for this. Our pseudo-SQL was:
This leads to the product details page. For this page we wanted:
This one is a little more complex. We want to return all of the XML doc for a partic-
ular product. As you learned in Chapter 11, “Retrieving XML,” you can’t just push the
XML back as a string because the markup would be escaped. You need to use the htp
and htf packages in conjunction with xsql:include-owa element get the XML into
the XSQL output. There are two ways to do this. If you are certain that the document
contains less than 4,000 characters, you can do it with the following procedure:
If there is any chance that you are going to have more than 4,000 characters in your
document, then you need to use CLOB as the data type for output_var. However,
htp.prn won’t take a CLOB as a parameter. You can still get the data to the output
using the following technique:
The second procedure is certainly safer. Even if you can require that an XML docu-
ment that you are using contains under 4,000 characters, you still need to make sure
that larger documents don’t make it into the database. If they do, then you risk gener-
ating errors at runtime.
Back to the product details query. In the pseudo-SQL, we had only one query. When
implementing the query, it should be broken into two: (1) a procedure call using the
xsql:include-owa action and (2) a SELECT statement for the other data. The
SELECT statement looks like the following:
SELECT id,name,price
FROM product
WHERE id={product_id}
The text search query was a bit challenging because of the need for a procedure. The
next query is also interesting because we must handle the many-to-many relationship
between product categories and products. Our pseudo-SQL looked like this:
The actual SQL must involve the joiner table because a particular product may belong
to more than one category for the purposes of browsing. Here is how to write the SQL:
If a user was limited to finding a product in only one product category, the SELECT
statement would just look up that ID in the product table. If the query needs to join the
product_category and the product tables, you would join on the category ID of
those two tables. You’ll see an example of this when you implement the price editor.
SELECT p.name,
pc.name
FROM product p, prod_cat_joiner pcj, product_category pc
WHERE p.id=pcj.product_id
AND
pcj.product_cat_id=pc.id
AND
pc.id={@category_id};
The next SQL needed is for the navigational item on the left side of the subordinate
pages. The pseudo-SQL looked like this:
SELECT id,name,description
FROM product_category
ORDER BY name
As mentioned before, the name of the product category that appears at the top of the
details page is tricky. Because you can get to a product from multiple product cate-
gories, you can’t simply look up the correct product category based on the product ID.
Instead, you have to pass the product category ID as a parameter to the product details
page. From the product details XSQL, you can access the product category name with
the simple SELECT statement:
SELECT name
FROM product_category WHERE id={@category_Id}
396 Chapter 14
The remaining SQL statements are used with the edit functionality. The first step is
to locate the products in which the editor is interested, as described by the pseudo-
SQL:
In the case of editing, you don’t want the search query to generate too many false
positives, so you can use a SQL statement that just searches against the name:
After someone selects a product, then they will need to get the product information
based on the product ID. They can do this with this SQL:
This leaves one final SQL statement: the update statement required for our price
editor. The pseudo-SQL looked like this:
UPDATE product
SET price={@new_price}
WHERE id={@product_id}
Now you have all of your SQL statements necessary for the application. If your
application were a sandwich, you would have both pieces of bread. You know from the
mockups what the application should look like, and now you have the database along
with all of the SQL. The last step is the meat of the application: You create the XSQL
pages along with the XSLT.
Building XSQL Web Applications 397
<?xml version=”1.0”?>
<xsql:query connection=”momnpup” xmlns:xsql=”urn:oracle-xsql”>
SELECT id,name FROM product_category
</xsql:query>
This looks just like all of the other XSQL that you’ve seen as you’ve learned the
basics of XSQL. But now, you are developing not just a single XSQL page but a series
of XSQL pages. XSLT tends to work best on well-thought-out and well-organized XML
398 Chapter 14
input documents. Because you are including this page within other XSQL documents,
you will have multiple rowset and rowelements relating to completely different data.
Using the rowset-element and the row-element parameters, you can better orga-
nize your data:
<?xml version=”1.0”?>
<xsql:query
connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”
rowset-element=”PRODUCT_CATEGORIES”
row-element=”CATEGORY”>
SELECT * FROM product_category
</xsql:query>
The next page to complete is the promotion page. It is only used once in our site, but
it has the aspect of its own element. Using the same technique as before, you can create
it so that it can fit anywhere in the site.
<?xml version=”1.0”?>
<xsql:query
connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”
rowset-element=”PROMOS”
row-element=”PROMO”>
SELECT url,product_id
FROM promotion
WHERE status=’ACTIVE’
ORDER BY slot
</xsql:query>
Now you can start with the development of the home page. The home page has the
two queries: (1) product categories with their descriptions and (2) the promo page.
You’ll include the preceding promo page, so this leaves only one query that will be in
the page.
<?xml version=”1.0”?>
<home connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
<xsql:query rowset-element=”PRODUCT_CATEGORIES”
row-element=”CATEGORY”>
SELECT id,name,description FROM product_category
</xsql:query>
<xsql:include-xsql href=”promo.xsql”/>
</home>
Building XSQL Web Applications 399
The next page is the product list page. Using the SQL developed earlier, it appears
as follows:
<?xml version=”1.0”?>
<cat-list connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
<xsql:include-xsql href=”cat-nav.xsql”/>
<xsql:query rowset-element=”PRODUCTS”
row-element=”PRODUCT”>
SELECT p.id AS product_id,
p.name AS product_name,
p.doc.extract(‘/product/summary/text()’).getStringVal() AS
summary
FROM product p,prod_cat_joiner pcj
WHERE pcj.product_cat_id={@category_id} AND p.id=pcj.product_id
</xsql:query>
</cat-list>
We can improve on this a bit. When developing the SQL, we used the standard
parameter interpolation syntax. However, as discussed in Chapter 8, it is more efficient
to use bind parameters. Here is the revised SQL using bind parameters. It also
sets a default parameter for category_id in case the page is called without the
category_id parameter.
<?xml version=”1.0”?>
<cat-list connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
<xsql:include-xsql href=”cat-nav.xsql”/>
<xsql:query rowset-element=”PRODUCTS”
row-element=”PRODUCT”
category_id=”0”
bind-params=”category_id”>
SELECT p.id AS product_id,
p.name AS product_name,
p.doc.extract(‘/product/summary/text()’).getStringVal() AS
summary
FROM product p,prod_cat_joiner pcj
WHERE pcj.product_cat_id=? AND p.id=pcj.product_id
</xsql:query>
</cat-list>
Now you’re down to the product details page. The following XSQL page gets the
information about a particular product using two different actions. A query gets the id,
400 Chapter 14
name, and price while the xsql:include-owa action is used to return the XML doc-
ument using the get_product_xml procedure used earlier.
<?xml version=”1.0”?>
<prod-details connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
<xsql:include-xsql href=”cat-nav.xsql”/>
<xsql:query rowset-element=”PRODUCT-SET”
row-element=”DETAILS”
product_id=”0”
bind-params=”product_id”>
SELECT id,name,price FROM product WHERE id=?
</xsql:query>
<xsql:include-owa product_id=”0” bind-params=”product_id” >
get_product_xml(?);
</xsql:include-owa>
</prod-details>
The last page for the public interface is the search results page. It uses the query
developed earlier and, like our other subordinate pages, includes the navigational ele-
ment on the left.
<?xml version=”1.0”?>
<prod-search connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
<xsql:include-xsql href=”cat-nav.xsql”/>
<xsql:query rowset-element=”PRODUCT_SEARCH”
row-element=”PRODUCT”>
SELECT id, name,
a.doc.extract(‘/product/summary/text()’).getStringVal() AS summary
FROM product a
WHERE contains(doc,’{@search_terms}’)>0
ORDER BY contains(doc,’{@search_terms}’)
</xsql:query>
</prod-search>
You have three XSQL pages for the price editor interface. The first displays the nec-
essary information in response to a lookup:
<?xml version=”1.0”?>
<prod-search connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
<xsql:query rowset-element=”PRICE_EDITOR_SEARCH”
row-element=”PRICE_EDITOR_PRODUCT”>
SELECT p.name AS product_name,
pc.name AS product_cat_name,
p.price
FROM product p, prod_cat_joiner pcj, product_category pc
WHERE p.id=pcj.product_id
AND
pcj.product_cat_id=pc.id
Building XSQL Web Applications 401
AND
p.name like ‘%{@terms}%’
</xsql:query>
</prod-search>
Your second XSQL page is very similar to the product details page. The information
for this page will be used to fill the values for the fields of a particular editor.
<xsql:query connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”
rowset-element=”PRODUCT-SET”
row-element=”DETAILS”
product_id=”0”
bind-params=”product_id”>
SELECT id,name,price FROM product WHERE id=?
</xsql:query>
The last page to create is the page that handles the price change:
Now you have all of your XSQL pages created. You can test them using URLs to
make sure that you are getting the results you want back. In the next section, the
stylesheets are created to transform the raw data into something useful for your users.
stylesheets for the price editor will be developed. Two points of functionality are cov-
ered in later sections of this chapter: (1) the stateless paging of product result sets and
(2) parameter passing. In reality, you’ll probably want to solve those problems at the
same time as the stylesheet problems that you’ll be solving here. However, from a dis-
cussion standpoint, it is far easier to cover those topics in their own sections.
N OT E Before going any further, there is one fact of XSQL of which you should
be aware. Regardless of how you set the rowset and row element names in
your XSQL, they will be uppercase in your output. In the preceding examples
they were always set uppercase, so you won’t notice the difference. Because
XML is case sensitive, though, it’s important to be aware of this before you
start writing stylesheet code. If you specify lowercase rowset and row
element names in your XSQL and then write your stylesheets expecting those,
your stylesheets won’t work. The solution is simple—just uppercase the element
names in your stylesheet expressions.
First on the hit list is the home page. As with all of the XSQL pages, your first step is
to link to the stylesheet in your XSQL. For home.xsql, you should add the following
as the second line:
Now it’s time to create the stylesheet itself. The following code is the top of the doc-
ument and the main template that is invoked for the top-level element, “home”. There
are other templates to add, so you don’t have a closing stylesheet tag yet.
<!--promotion table-->
<xsl:apply-templates select=”PROMOS”/>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
As promised, there are no problems integrating CSS with XSLT. We’ll use references
to CSS styles throughout the code examples. Working down through the example, the
next thing to notice is the xsl:include of banner.xsl. The banner.xsl stylesheet
contains a single template-named banner. It is called with an xsl:call-template
element. Notice that no parameters are passed to it. Here we are using XSLT for a sim-
ple purpose—HTML reuse. By separating the banner into its own file, it can be called
from all of our files. If we want to change it, we can change it once, and it will be
changed throughout the site. The fact that it doesn’t actually interpolate any of the
inputted XML data doesn’t actually matter. Here is the banner.xsl stylesheet:
The banner code itself isn’t particularly interesting. There aren’t even any XSLT
elements besides xsl:stylesheet and xsl:template. It could, of course, contain
any XSLT elements. However, because xsl:call-template is used to invoke the
template, you don’t know what the context node will be, and thus your XPath expres-
sions should be absolute. As discussed in Chapter 13, you can pass parameters to a
template that is invoked with xsl:call-template and have functionality similar to
a subroutine.
The one thing to note in the banner.xsl is the search field. It links to the prod
-search.xsql page using a post query. You’ll develop that stylesheet in another cou-
ple of pages. For now, it’s time to round out the home.xsl stylesheet that we started
earlier. Looking at the top-level template for home.xsql, you can see that two other
templates are invoked with apply-templates. These templates in turn invoke their
own templates. Let’s start with the “PROMOS” template and its associated template:
<xsl:template match=”PROMOS”>
<table border=”0”>
<xsl:apply-templates select=”PROMO”/>
</table>
</xsl:template>
<xsl:template match=”PROMO”>
<tr>
<td width=”200” height=”100”>
<a>
<xsl:attribute name=”href”>prod-details.xsql?product_id=<xsl:value-
of select=”PRODUCT_ID”/></xsl:attribute>
<img width=”200” height=”100”>
<xsl:attribute name=”src”>
<xsl:value-of select=”URL”/>
</xsl:attribute>
</img>
</a>
</td>
</tr>
</xsl:template>
Looking back at promo.xsql, you’ll see that it pulls two pieces of data on the
promo: (1) the product ID and (2) the URL for the promo image. The image is assumed
to be 200 x 100. A link is set around the image so that when you click on the image you
are taken to the product details page.
This template has the first of many uses of the xsl:attribute element to set the
value of an HTML attribute. You can’t use an XSLT element inside of an XML element,
like <a>, so you have to set the attribute separately. You may notice that the xsl:
attribute code isn’t nicely tabbed like the rest of the example. This is purposeful; If
you put the xsl:value-of element on its own line, then there will be white space
inside of your attribute value. You should always keep all of an xsl:attribute
element on one line.
The “PRODUCT CATEGORIES” template is the most complex in our entire applica-
tion. There are several challenges. First, the categories are separated into columns with
Building XSQL Web Applications 405
the first categories appearing in the left-hand column and the later categories appear-
ing in the right-hand column. This is more complex than listing the columns in a left-
to-right ordering. To solve this, we have two separate tables nested into a higher-level
table consisting of only two cells. The xsl:apply-templates is called first on all
categories that are in the first half of the result set, and then a separate xsl:apply
-templates is called on all categories in the second half of the result set. If there is an
odd number of categories, the right-hand column will have the extra.
<xsl:template match=”PRODUCT_CATEGORIES”>
<table border=”0”>
<tr>
<td valign=”top”>
<!-- left hand table -->
<table valign=”top” width=”300” border=”0”>
<xsl:apply-templates select=”CATEGORY[position() <= (last() div
2) ]”/>
</table>
</td>
<td valign=”top”>
<!-- right hand table -->
<table valign=”top” width=”300” border=”0”>
<xsl:apply-templates select=”CATEGORY[position()>(last() div 2) ]”>
<xsl:with-param name=”align-val”>right</xsl:with-param>
</xsl:apply-templates>
</table>
</td>
</tr>
</table>
</xsl:template>
Notice that a parameter is passed for the right-hand categories. This is because the
mockup requires that the text in the left-hand column be left-adjusted, and the text in
the right-hand column be right-adjusted. You could develop two templates—one for
each column—but most all of the code would be redundant. Instead, you simply pass
a parameter and set the “align” attribute based on the parameter. Because the default
value of the parameter is “left”, it isn’t necessary to pass a parameter for the preceding
left-hand apply-templates. Here is the “CATEGORY” template that is invoked.
<xsl:template match=”CATEGORY”>
<xsl:param name=”align-val”>left</xsl:param>
<tr>
<td>
<xsl:attribute name=”align”><xsl:value-of select=”$align-
val”/></xsl:attribute>
<span class=”category”>
<a>
<xsl:attribute name=”href”>prod-cat.xsql?category_id=<xsl:value-
of select=”ID”/></xsl:attribute>
<!-- product name -->
406 Chapter 14
For this template you pull the text values for the name and description elements for
the category and link them to the product category page. You also have to list the first
products in the category. The template for this follows. It solves two problems. First,
only the first three elements are pulled while the rest are ignored. Second, commas are
only inserted between elements 1 and 2 and elements 2 and 3. This is a common prob-
lem in XSLT and is easily solved with an xsl:if element along with the position
and last functions. The individual product names are linked to the product pages.
<xsl:template match=”PRODUCTS”>
<xsl:for-each select=”PRODUCTS_ROW[position() <= 3]”>
<a>
<xsl:attribute name=”href”>prod-details.xsql?product_id=<xsl:value-of
select=”./ID”/></xsl:attribute>
<xsl:value-of select=”./NAME”/>
<xsl:if test=”position() < last()”>, </xsl:if>
</a>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The last tag is the close of our stylesheet for the home.xsql. You’ll now have a
home page that looks like Figure 14.1 that appeared earlier in this chapter, but now all
of the data is database driven. What’s really neat is that all of the links and search func-
tionality work. You still get back the ugly XML, but you are getting back actual data
from the database. Now, it’s time to make the rest of the application pretty.
The search results page and the product category page present essentially the same
set of challenges. We might as well start with the product category page. On the left of
the page is the category directory. As with the banner template, the template for the
category directory will be separated into its own file. The main stylesheet along with
the stylesheet header is as follows:
Building XSQL Web Applications 407
First, notice that the top-level template references a different element than
home.xsl. The top-level home.xsql page outputs “home” as the top-level element,
whereas the top-level element here is “prod-cat”. By having a different root element
for each XSQL page, you are able to build distinct templates for the different types of
data. If you ever need to intermingle the templates, it will be easier to do so.
408 Chapter 14
As with the home page, the banner template is invoked to create the banner at the top
of the page. The product-categories template is in the cat-nav.xsl stylesheet
covered later. It handles the directory listing on the left-hand side of the page.
From there, the template precedes simply. The category name is pulled from the
XML, and then the template for the set of products is invoked. For the category name
we use the // to mean “child-or-descendant”. Because this axis appears at the begin-
ning of the expression, it means child or descendent of root. This syntax is a little dan-
gerous because it assumes that there is only one element of that name in the entire XML
document. In our case we know for certain that there is only one CATEGORY_NAME, so
it’s okay. The alternative would be to spell out multiple layers in order to access this one
data point. XSQL always expects there to be more than one row, so the XML is always
structured that way. However, if there really is only one row, it’s just another layer of
elements that needs to be navigated. Used with caution, the child-or-descendant axis
can make your code a little more readable when you are only pulling one row of data.
The products template and its child template, called”PRODUCT”, follow. These are
fairly straightforward. For each product in the result set, the product name and sum-
mary are listed. The product names are linked to the respective product details pages.
As this is the end of the stylesheet, the end tag is also included.
<xsl:template match=”PRODUCTS”>
<table class=”search-results” width=”100%”>
<th><span class=”search-results”>Product</span></th><th>Summary</th>
<xsl:apply-templates select=”PRODUCT”/>
</table>
</xsl:template>
<xsl:template match=”PRODUCT”>
<tr>
<td>
<a>
<xsl:attribute name=”href”>prod-details.xsql?product_id=<xsl:value-
of select=”PRODUCT_ID”/></xsl:attribute>
<xsl:value-of select=”PRODUCT_NAME”/>
</a>
</td>
<td><xsl:value-of select=”SUMMARY”/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
Before moving onto the product search page, we need to back up and do the cat-
egory navigation page. It is a straightforward stylesheet that matches up with the
cat-nav.xsql page. The cat-nav.xsql page furnishes the XML as a top-level
element, and the cat-nav.xsl page takes that XML and formats into a list of linked
category names.
On to the search results page! The search results page is very similar to the product
category page and its full display follows. The one difference is that the linked category
name for a product is included in the results. The primary category for the product is
used.
<table>
<tr><td><a href=”home.xsql”>Catalogue Home Page</a></td></tr>
<tr><td>Search Results For: </td></tr>
<tr><td>
<!--search results-->
<xsl:apply-templates select=”PRODUCT_SEARCH”/>
</td></tr>
</table>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
<xsl:template match=”PRODUCT_SEARCH”>
<table class=”search-results” height=”100%” width=”100%”>
<th><span class=”search-
results”>Product</span></th><th>Summary</th><th>Category</th>
<xsl:apply-templates select=”PRODUCT”/>
</table>
</xsl:template>
<xsl:template match=”PRODUCT”>
<tr>
<td>
<a>
<xsl:attribute name=”href”>prod-details.xsql?product_id=<xsl:value-
of select=”PRODUCT_ID”/></xsl:attribute>
<xsl:value-of select=”PRODUCT_NAME”/>
</a>
</td>
<td><xsl:value-of select=”SUMMARY”/></td>
<td>
<a>
<xsl:attribute name=”href”>prod-cat.xsql?category_id=<xsl:value-of
select=”CATEGORY_ID”/></xsl:attribute>
<xsl:value-of select=”CATEGORY”/>
</a>
</td>
</tr>
</xsl:template>
</xsl:stylesheet>
Now it’s time to finish the application by creating the product details stylesheet. It
includes the cat-nav.xsl and the banner.xsl as with the two previous
stylesheets. It also has a couple of tricks up its sleeve. The other stylesheets dealt with
pretty standard entities—names and short sentences. This page must render images of
varying sizes and XML documents of varying content. Though it is a little complex, the
prod-details.xsl stylesheet handles it all with elegance and shows you how pow-
erful XSLT can be. Here are the headers and the main template:
Building XSQL Web Applications 411
This template calls three templates. The first two are the old friends, banner and
product categories. The meat of the work is done in the “PRODUCT-SET/DETAILS”
template. Before examining that template, please note that one of our missing pieces of
412 Chapter 14
functionality is on this page. The category from which this product was reached (or the
primary category if it was reached through a promo or the search engine) is missing.
This will be covered in the next discussion. For now, let’s look at the key template for
this page. The code follows:
<xsl:template match=”PRODUCT-SET/DETAILS”>
<tr>
<td align=”left”>
<span class=”product-name”>
<xsl:value-of select=”NAME”/>
</span>
</td>
<td align=”right”>
<span class=”product-price”>
$<xsl:value-of select=”PRICE”/>
</span>
</td>
</tr>
<tr><td colspan=”2”>
<xsl:choose>
<xsl:when test=”WIDTH>100”>
<xsl:apply-templates select=”IMAGE_URL”>
<xsl:with-param name=”align-var”>top</xsl:with-param>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select=”IMAGE_URL”/>
</xsl:otherwise>
</xsl:choose>
<xsl:apply-templates select=”//product/*”/>
</td></tr>
</xsl:template>
The first thing to note is the match for this template. All of our other templates
matched an element one level down, but this one goes two levels down. The data we
want is two levels down, so we just skip a level. Once in the DETAILS element, we
have a couple of jobs to do. The name and the price need to be displayed at the top
level. Underneath that, an image needs to be displayed along with XML elements of
the product document.
The image is our first challenge because it can vary in size. If it is less than 300 pix-
els wide, then the text should be on the left. If it is over 300 pixels wide, then the text
should be underneath. An xsl:choose element is used to set a parameter to affect the
appropriate behavior. After the image is set up, the XML document from our database
is processed. Before getting to that, let’s look at the IMAGE_URL template.
<xsl:template match=”IMAGE_URL”>
<xsl:param name=”align-var”>right</xsl:param>
<xsl:if test=”$align-var=’top’”>
<xsl:text disable-output-escaping=”yes”><center></xsl:text>
Building XSQL Web Applications 413
</xsl:if>
<img>
<xsl:attribute name=”align”><xsl:value-of select=”$align-
var”></xsl:value-of></xsl:attribute>
<xsl:attribute name=”height”><xsl:value-of
select=”../HEIGHT”/></xsl:attribute>
<xsl:attribute name=”width”><xsl:value-of
select=”../WIDTH”/></xsl:attribute>
<xsl:attribute name=”src”><xsl:value-of select=”.”/></xsl:attribute>
</img>
<xsl:if test=”$align-var=’top’”>
<xsl:text disable-output-escaping=”yes”></center></xsl:text>
</xsl:if>
</xsl:template>
Most of the code in this template sets up the img element. The align attribute is set
by the align-var variable that is passed in, or is set to “right” if no variable is
passed in. The width, height, and src attributes are also set. In the case where the image
should appear at top, it should be centered. The xsl:if element is used to determine
when the beginning center tag and the closing center tag should be used.
This is a ticklish case because we run into the old problem of inserting XML ele-
ments into the document. If we tried to do:
<xsl:if test=”$align-var=’top’”>
<center>
</xsl:if>
then our document isn’t valid XML, and the stylesheet processor will reject it. We
could do:
<xsl:choose>
<xsl:when test=”$align-var=’top’”>
<center>
. . . bunch o’ code
</center>
</xsl:when>
<xsl:otherwise>
. . . same bunch o’ code
</xsl:otherwise>
</xsl:choose>
but then we have a lot of redundant code. The workaround is to use the xsl:text ele-
ment with disable-output-escaping on. The burden is on us to make sure that the end
result is well-formed XML.
The last set of templates is in its own separate file. They don’t have to be, but it
allows our overall code to be a little neater. These templates match against the XML ele-
ments in the XML product document. The idea is that the elements of the XML docu-
ment can vary from product to product. Clothing has sizes, but soccer goals don’t. We
414 Chapter 14
could do a lot of logical processing to handle all of the different permutations. Or, we
can take an XML push approach. Before explaining further, let’s look at the code:
So far, all of our templates have been used. In the case of these product-specific XML
documents that are being passed to us, some of the templates won’t be used. We can
assume that summary and description will be present for all of the items. However,
you wouldn’t provide dimensions for a sweater, and you wouldn’t provide the cloth-
ing size for a dresser. Instead, the XML elements in a product’s document are matched
to the appropriate templates. If a new product comes along whose XML document has
a new element, you simply write up a template for it and put it in this document. You
Building XSQL Web Applications 415
set up a style for it to be consistent with the rest of the templates, and you’re done. If
you have elements that you don’t want displayed, they won’t be because of the wild-
card element at the bottom.
What this approach doesn’t allow for is the complete reformatting of a page based
on the XML document. Also, the elements will be displayed in document order. If the
summary in a particular document appears at the bottom of an XML document in the
database, it will appear at the bottom of the page in our application. If you can’t
depend on the order, you can simply use individual xml:apply-templates to force
the order. Then you are back to requiring foreknowledge of all the XML documents’
structures and you lose the flexibility of our looser approach.
The public application is mostly complete. You can browse to your heart’s content.
If you stick new data into the database, then it should immediately appear in the appli-
cation. The following sections in this chapter, “Passing Parameters,” “Stateless Pag-
ing,” and “Error Handling,” will fullfill the rest of the requirements for the public
application.
Passing Parameters
HTTP was originally designed as a stateless protocol. An HTTP transaction performs a
single action, such as getting a file or posting HTML form data, and then it ends. This
is both a great strength and a great weakness. Because the transactions end immedi-
ately after the data is delivered back to the client, HTTP is very lightweight on the net-
work. Your HTTP-based applications can easily scale to large numbers of users
because very few transactions are alive at any given point in time.
When developing applications, though, the stateless nature of HTTP comes at a cost.
You have data on one page that needs to be passed to another page. A lot of the chal-
lenge of Web development is figuring out the best way to pass information between
two completely different transactions. The facilities that XSQL has for this were dis-
cussed in detail in Chapter 9. Now you’ll see how these can be applied in the real
world. In this section, you’ll mend up some of the holes of our application, and you’ll
learn some new lessons. You’ll immediately apply those lessons in this chapter—first,
when you solve the pagination problems, and then when you develop the price editor
part of the sample application.
For now, you’ll focus on the techniques of passing parameters. The first technique is
the simplest: You simply pass your parameter as part of an already existing SQL query.
Then you’ll look at the other two important techniques to setting parameters: (1) set-
ting stylesheet parameters and (2) using hidden-form variables. Later in this chapter,
you’ll also see how to set parameter values directly into JavaScript.
used this technique on the product category page with the following XSQL on the
prod-cat.xsql:
<xsql:query rowset-elements=”CATEGORY_ROWSET”
row-element=”CATEGORY_ROW”
category_id=”0”
bind-params=”category_id”>
SELECT id AS category_id,
name AS category_name
FROM product_category WHERE id=?
</xsql:query>
If you look back at that page, you’ll see that it makes perfect sense to pull the cate-
gory name. We need it to display at the top of the page. But why pull the ID for the cat-
egory? It doesn’t need to be displayed anywhere. The reason is that it needs to be
passed to the prod-details.xsql page because the selected category name needs
to be displayed on the next page. Because the name will also be linked back to the
product home page, the ID needs to be passed. This allows you to change the product
template of the prod_cat.xsl stylesheet as follows:
<xsl:template match=”PRODUCT”>
<tr>
<td>
<a>
<xsl:attribute name=”href”>prod-details.xsql?product_id=<xsl:value-
of select=”PRODUCT_ID”/>&category_id=<xsl:value-of
select=”//CATEGORY_ID”/>&</xsl:attribute>
<xsl:value-of select=”PRODUCT_NAME”/>
</a>
</td>
<td><xsl:value-of select=”SUMMARY”/></td>
</tr>
</xsl:template>
Now, you’re passing the parameter in the simplest way possible: embedded in a
hyperlink. On the product details page, the following code changes so that you get the
name and link to the category at the top of the page:
<tr><td>
<a href=”home.xsql”>Home</a>:
<a>
<xsl:attribute name=”href”>prod-cat.xsql?category_id=<xsl:value-of
select=”//CATEGORY_ID”/>&</xsl:attribute>
<xsl:value-of select=”//CATEGORY_NAME”/>
</a>
</td></tr>
You certainly don’t have to have a SQL statement for your parameters to latch on to.
Before worrying with how to pass a parameter on from a page, however, you should
take a close look at the where clauses of the SQL statements already on that page. It’s
Building XSQL Web Applications 417
the nature of database applications that the parameters you want to pass are already
being used in a where clause. In such cases, the simplest solution is to also specify them
as a field that is returned.
On the prod-search.xsl stylesheet, you need to add the following line. The
xsl:param should be a direct child of the root stylesheet element:
<xsl:param name=”search_terms”></xsl:param>
Further down in the stylesheet, you add the following code in order to get the search
terms:
<tr>
<td>
Search Results For: <xsl:value-of select=”$search_terms”/>
</td>
</tr>
As was covered in previous chapters, you can also set the value of stylesheet para-
meters based on SQL statements. They still can only hold a single value, though—if
your SQL statement returns multiple rows and columns, all but the first row and first
column are ignored.
418 Chapter 14
Stylesheet parameters are ideal for when you have just a couple of pieces of infor-
mation that need to be passed onto the next page. You can easily reference the values
by using the $ operator. If they need to be passed onto another page, it is easy to embed
them in a link or put them in a form parameter. You’ll be making more use of stylesheet
parameters when you build the price editor.
<xsql:set-session-param name=”category_name_sess”
category_id=”0”
bind-params=”category_id”>
SELECT name FROM product_category WHERE id=?
</xsql:set-session-param>
<xsql:set-cookie name=”category_name_cookie”
category_id=”0”
bind-params=”category_id”>
SELECT name FROM product_category WHERE id=?
</xsql:set-cookie>
Unlike in the earlier solution, you don’t need to make any modifications to the
prod-cat.xsl stylesheet. With the previous approach, parameters are passed by
embedding them in the HTTP request. In the preceding case, they were passed in the
URL. In the case where a POST request is used, you can pass them as hidden-form vari-
ables. In these cases the parameters sit outside of the request. The cookie lives on the
user’s browser and is sent back to the server on each request, based on the domain
parameter of the xsql:set-cookie action. The session parameter actually lives on
the server but is tied to a cookie on the user’s browser. In either case, all you have to do
is ask for them when you need them. You can do this regardless of where you are in the
application. This is how you would get those values to pop up on the product details
page. First, put the following anywhere in your prod-details.xsql page:
<xsql:include-param name=”category_name_cookie”/>
<xsql:include-param name=”category_name_sess”/>
Building XSQL Web Applications 419
The values will be placed in XML elements in the output named category_name_
cookie and category_name_sess, respectively. You can grab them using the
following code in your XSLT stylesheet:
You may look at this code and think, “That looks simple. Why not do that all the
time?” There are a couple of problems with this approach. First, both session and
cookie parameters assume that the user will accept cookies from you. A lot of applica-
tions assume this—there’s no big whoop in requiring this from your users.
The second problem requires more consideration. Sessions and cookies behave like
global variables in procedural programming. Global variables are generally frowned
upon—it’s hard making sure that all of the different modules are setting and unsetting
them as expected. In fact, our solution has such a problem. If someone goes to a product
category page and then doesn’t go on to a product details page, the category name is still
set. If they then access the page from a different route (e.g., through a promo or through
a search), the category name is still set. Then you have a different kind of problem.
In the previous example, you dealt with cookies and session parameters whose
names you know. It is sometimes good to deal with parameters more generally. For
instance, you may want to pass all of the parameters to a servlet and let the servlet fig-
ure out what to do with them. The following example shows you an easy way to deal
with all of the parameters without knowing any of their names at development time.
To demonstrate this, you first need an XSQL page that sets some parameters, though it
doesn’t matter what their names are:
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”params.xsl”?>
<page xmlns:xsql=”urn:oracle-xsql” connection=”demo”>
<xsql:set-cookie name=”long-living-cookie” max-age=”999999”
value=”To_Life” />
<xsql:set-cookie name=”cookie-deptno”>
select deptno from emp where ename=’{@ename}’
</xsql:set-cookie>
<xsql:set-session-param name=”session-sal”>
select sal from emp where ename=’{@ename}’
</xsql:set-session-param>
<xsql:include-request-params />
</page>
Now you want to be able to access all of these values in a stylesheet. Here’s a simple
stylesheet that will display all of your values:
<head></head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match=”request”>
<b>Request:</b>
<xsl:apply-templates select=”*”/>
</xsl:template>
<xsl:template match=”session”>
<b>Session:</b>
<xsl:apply-templates select=”*”/>
</xsl:template>
<xsl:template match=”cookies”>
<b>Cookies:</b>
<xsl:apply-templates select=”*”/>
</xsl:template>
<xsl:template match=”*”>
<p>
Name: <xsl:value-of select=”name(.)”/>
Value: <xsl:value-of select=”.”/>
</p>
</xsl:template>
</xsl:stylesheet>
This will give you a listing of all of the parameters, regardless of their names. Exam-
ple output for this is shown in Figure 14.11.
From here, it is a simple matter to create a set of hidden-form parameters that hold
these values. Then you can use easily pass the values on as form submissions, or use
them in JavaScript. Here is how you do it:
<xsl:template match=”*”>
<input type=”hidden”>
<xsl:attribute name=”name”><xsl:value-of
select=”name(.)”/></xsl:attribute>
<xsl:attribute name=”value”><xsl:value-of
select=”.”/></xsl:attribute>
</input>
You’ll see more examples of hidden-form variables and the role they can play
in JavaScript and XSLT integration when you work with the price editor later in the
chapter.
Building XSQL Web Applications 421
Stateless Paging
So far, the product catalog doesn’t have a lot of data in it. A list of all of the products in
the catalog probably still wouldn’t necessitate a scroll bar in the browser window.
However, what if your queries could easily return thousands of rows? Even if your
queries are just returning tens of rows, you want your users to be able to navigate
through them in a reasonable manner. This is where paging comes in. Instead of show-
ing all of the results on one page, you see just a set of results, and then you can navi-
gate forward and backward to other sets of results. This section looks at the challenges
of pagination from a high level and then shows you how to apply paging to the search
screen of the catalog application.
422 Chapter 14
The solution that will be provided here is a pure XSQL/XSLT solution. The example
uses a recursive technique and really stretches the capabilities of XSLT. However, when
you find yourself stretching too hard, it is often good to ask, “Is there a better way to
do this?” In this case, the answer is probably yes.
Challenges of Pagination
Paging offers a simple solution to a common problem. What do you do when you get
a lot of results back in your search? You don’t want to try to force them all into one
browser window. For particularly big searches the browser might run out of memory
first. Instead, you want to separate the queries over pages.
The next question is: How do you separate a query into a lot of different pages? You
definitely don’t want to keep a connection to the database server open. It doesn’t scale.
You’ll be keeping connections open for indefinite periods of time for all of the users
that come to the site. Besides, there isn’t a way to do it without a custom action handler,
anyway. Temporary tables are also a generally bad idea for this kind of thing. You
might be able to cobble a solution to the problem, but you’ll create big development
headaches for yourself. Not to mention, you’ll need some multiple of storage space to
keep all of the pages of results for the queries. As you get more queries, you’ll need
more storage space.
Fortunately, the xsql:query action offers an easy solution. You use the max-rows
and skip-rows attributes to describe your pages of a query. The max-rows attribute
sets the size of the page—how many rows you want the query to return. The skip
-rows attribute specifies where in the query result set you should start—the position
of the page.
This does mean, of course, that you will be doing the same query for each page. This
may seem expensive and redundant, but it is Oracle’s job to optimize queries and
make subsequent requests speedy. Besides, you simply can’t reasonably keep the data-
base connection open, and even if you tried to hack together a solution with temporary
tables, you still would be doing a query for every page.
this technique or hate it. In either case, it is an interesting exercise in how you can solve
problems in XSLT that are usually solved in XSLT. First things first, starting with the
XSQL. You’ll need to change your query to use skip-rows and max-rows as follows:
<xsql:query rowset-element=”PRODUCT_SEARCH”
row-element=”PRODUCT”
max-rows=”{@max-rows}”
skip-rows=”{@skip-rows}”>
SELECT p.id AS product_id,
p.name AS product_name,
p.doc.extract(‘/product/summary/text()’).getStringVal() AS
summary,
pc.id AS category_id,
pc.name AS category
FROM product p, product_category pc
WHERE contains(doc,’{@search_terms}’)>0
AND pc.id=p.category_id
ORDER BY contains(doc,’{@search_terms}’)
</xsql:query>
Now your query will work for the first page. Regardless of the search, you should
get at most four results. The next challenge is giving the user a way to navigate to the
other results. This is where the real XSLT adventures start. All of these modifications
will take place in prod-search.xslt. Your first step is to create top-level parameters
so you can receive the values that you set in prod-search.xsql:
<xsl:param name=”search_terms”></xsl:param>
<xsl:param name=”row-count”></xsl:param>
<xsl:param name=”skip-rows”></xsl:param>
<xsl:param name=”max-rows”></xsl:param>
Now you need to create a template that recreates the initial form using all hidden
parameters:
<xsl:template name=”paging-form”>
<xsl:param name=”new-skip-rows”></xsl:param>
<xsl:param name=”button-text”></xsl:param>
<form method=”post” action=”prod-search.xsql”>
<input type=”hidden” name=”skip-rows”>
<xsl:attribute name=”value”><xsl:value-of select=”number($new-skip-
rows)”/></xsl:attribute>
</input>
<input type=”hidden” name=”search_terms”>
<xsl:attribute name=”value”><xsl:value-of
select=”$search_terms”/></xsl:attribute>
</input>
<input type=”submit”>
<xsl:attribute name=”value”><xsl:value-of select=”$button-
text”/></xsl:attribute>
<xsl:if test=”$new-skip-rows=-1”>
<xsl:attribute name=”disabled”>yes</xsl:attribute>
</xsl:if>
</input>
</form>
</xsl:template>
You call this template with two parameters, new-skip-rows and button-text.
The new-skip-rows parameter specifies the position in the query that the user
should go to on pressing the form’s Submit button. The button-text parameter
specifies the text for the button. If the new_skip_rows parameter is set to -1, then the
Form button is disabled.
You can get around the need to use a Form button with a little JavaScript, but it’s dif-
ficult to get around the need to use a form. The query needs to be a POST query
because search_terms may have spaces and other funny characters in it. If you tried
to pass it as part of a query string, the link would break. In the next section, you’ll see
a way to encode values using XSLT extensions.
Now that you have your form, you need to link it to some navigational elements.
We’ll start simply with just Previous and Next buttons. The problem here is determin-
ing what the Previous and Next pages are and whether it is valid to enable the buttons.
Building XSQL Web Applications 425
If you are at the beginning of the result set, then you shouldn’t display a Previous but-
ton; if you are at the end of a result set, you shouldn’t display a Next button. Here is the
code that calculates the value of skip-rows for Previous and Next and determines
whether the buttons should be disabled:
<xsl:template name=”paging”>
<table><tr><td>
<xsl:choose>
<xsl:when test=”number($skip-rows)-number($max-rows)>=0”>
<xsl:call-template name=”paging-form”>
<xsl:with-param name=”new-skip-rows”><xsl:value-of
select=”number($skip-rows)-number($max-rows)”/></xsl:with-param>
<xsl:with-param name=”button-text”><<Previous</xsl:with-
param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name=”paging-form”>
<xsl:with-param name=”new-skip-rows”>-1</xsl:with-param>
<xsl:with-param name=”button-text”><<Previous</xsl:with-
param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</td>
<td>
<xsl:choose>
<xsl:when test=”number($skip-rows)+number($max-rows)<number($row-
count)”>
<xsl:call-template name=”paging-form”>
<xsl:with-param name=”new-skip-rows”><xsl:value-of select=”$skip-
rows+$max-rows”/></xsl:with-param>
<xsl:with-param name=”button-text”> Next>> </xsl:with-
param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name=”paging-form”>
<xsl:with-param name=”new-skip-rows”>-1</xsl:with-param>
<xsl:with-param name=”button-text”> Next>> </xsl:with-
param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</td></tr></table>
</xsl:template>
As you can see, you basically repeat the same code for the Previous and the Next
buttons. The only differences are the test conditions and the value that you pass to the
paging-form template. This yields the results that are shown in Figure 14.12.
426 Chapter 14
Now it’s time to spice up the navigation a bit. Instead of just being able to navigate
to the Previous or Next page, we want to be able to navigate to any page in our results.
The buttons taking us to those results will live between the Previous and Next buttons.
The first step is to add the following code right in the middle of the preceding paging
code. It should exist between the two cells where the Previous and Next buttons live:
<td>
<xsl:call-template name=”paging-list”/>
</td>
The final step is the creation of the paging-list template. The challenge is that
you have to somehow create buttons for an indeterminate number of pages. You’ll do
this using recursion—you’ll invoke the template from inside the template. Before look-
ing at that, though, let’s look at the basic stuff. The start of the template does the basic
mechanics of translating a page number into a skip-rows parameter and a button
name. The paging-form template is used to actually create the form and the button.
<xsl:template name=”paging-list”>
<xsl:param name=”counter”>0</xsl:param>
<td>
Building XSQL Web Applications 427
<xsl:choose>
<xsl:when test=”number($counter)*number($max-rows)!=number($skip-
rows)”>
<xsl:call-template name=”paging-form”>
<xsl:with-param name=”new-skip-rows”><xsl:value-of
select=”number($counter)*number($max-rows)”/></xsl:with-param>
<xsl:with-param name=”button-text”><xsl:value-of
select=”number($counter)+1”/></xsl:with-param>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name=”paging-form”>
<xsl:with-param name=”new-skip-rows”>-1</xsl:with-param>
<xsl:with-param name=”button-text”><xsl:value-of
select=”number($counter)+1”/></xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</td>
This code is very similar to the code for the Previous and Next buttons. It determines
what the new-skip-rows value should be and dynamically determines the name of
the button based on the counter. It also disables the button when it isn’t appropriate to
use it. In this case, the button for the current page is disabled.
If we stopped right here, you would have a single button for the first page. To get
buttons for all the pages, you need to recursively call the template. The following code
takes care of this. You first have to determine when the template should be invoked.
Then, you determine what the new counter value should be. The following code com-
pletes the template:
<xsl:if test=”number($counter+1)*number($max-rows)<number($row-
count)”>
<xsl:call-template name=”paging-list”>
<xsl:with-param name=”counter”><xsl:value-of
select=”number($counter)+1”/></xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
With all recursion there is the risk of an infinite loop, so it’s important that you
ensure that the recursion will stop. This is a combination of controlling when the tem-
plate will be invoked and passing to it parameters so that it will violate that criteria at
some point. The output of this particular template is shown in Figure 14.13.
Now you’ve seen the way to handle stateless paging without the use of an action
handler. You’ve also gotten to see XSLT recursion in action. You can do a lot of neat
stuff with recursion, but it can lead to confusing and hard to maintain code. When you
find yourself using recursion in XSLT, it’s a good time to ask yourself if what you are
doing can be better accomplished with an action handler.
428 Chapter 14
Editor Architecture
There are a lot of ways to design Web-based editors. In all cases, you have at least one
component that actually processes the data into the database. The other components
surround this action. There’s usually a component that locates the data to be edited,
Building XSQL Web Applications 429
and then there is an HTML form that actually allows the editing of data itself. In cases
in which you are inserting new data, the HTML form is blank and is linked to a com-
ponent that inserts data into the database.
For this example, we’re going for a simple interface. Instead of having multiple
pages through which a user has to navigate, there is a single interface. Figure 14.14
shows what the finished product will look like. The same interface is reloaded any time
you save a record.
The editor consists of three XSQL pages and a single stylesheet. The stylesheet con-
sists of one search form and a separate form for each record returned in the search. One
XSQL page, price-editor-query.xsql, is used to find the records that the user
wishes to edit and is included in the other two XSQL pages. The price-editor
-search.xsql is invoked when the user searches for items to edit, while the price
-editor-update.xsql actually updates the items in the database. After updating
the items in the database, the same query is performed again and the results are
reloaded. Both of the top-level stylesheets are linked to the same stylesheet, price
-editor.xsl. The diagram appears in Figure 14.15.
This is certainly not the only way to implement an editor in XSQL. A simpler archi-
tecture is to have three pages: (1) search, (2) details, and (3) update. However, because
we’re pretty familiar with XSQL and XSLT by now, this architecture presents some
interesting challenges. At the end of the day, it should be more appeasing to the
user, too.
XSQL Development
As in the previous section, it’s a good idea to develop the XSQL before the XSLT
stylesheets. By doing things in this order you can develop against live data. For
this example you have three XSQL pages to develop. It’s best to start with price
-editor-query.xsql because it is included by the other two. It does a wildcard
search against the product name:
<?xml version=”1.0”?>
<xsql:query
connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”
rowset-element=”PRICE_EDITOR_QUERY”
row-element=”PRICE_EDITOR_PRODUCT”>
SELECT p.id AS product_id,
p.name AS product_name,
pc.name AS product_cat_name,
p.price AS product_price
FROM product p, product_category pc
WHERE pc.id=p.category_id
AND
p.name like ‘%{@terms}%’
AND
length(‘{@terms}’)>0
</xsql:query>
Building XSQL Web Applications 431
The second XSQL page is the page that is called after the user enters a search term.
It essentially wrappers the price-editor-query.xsql page and also passes the
term parameter to the stylesheet:
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”price-editor.xsl”?>
<prod-search connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
<xsql:set-stylesheet-param name=”terms” value=”{@terms}”/>
<xsql:include-xsql href=”price-editor-query.xsql”/>
</prod-search>
The last XSQL page is responsible for updating the database. After updating the
database, it repeats the earlier query so that you can edit more prices that are part of the
same query.
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”price-editor.xsl”?>
<price-update xmlns:xsql=”urn:oracle-xsql” connection=”momnpup”>
<update-results>
<xsql:dml commit=”yes”
product_id=”0”
bind-params=”new_price product_id”>
UPDATE product
SET price=?
WHERE id=?
</xsql:dml>
</update-results>
<xsql:dml>COMMIT</xsql:dml>
<xsql:set-stylesheet-param name=”terms”
value=”{@terms}”/>
<xsql:set-stylesheet-param name=”updated_product”
value=”{@product_name}”/>
<xsql:include-xsql href=”price-editor-query.xsql”/>
</price-update>
This XSQL page takes new_price and product_id as parameters for the update
statement and also needs the terms query in order to requery the database.
The first step is to handle the two different top-level elements. This is accomplished
by using an ‘or’ operator in the select XPath expression at the top level of the
stylesheet. It will match on documents with either prod-search or price-update
as their root element. It would be possible to give both XSQL pages the same root ele-
ment, but it isn’t a good practice. Though very similar, the two different XML schemas
are distinct.
The head contains a large section of JavaScript that is omitted for now. The next sec-
tion of the chapter looks in depth at the JavaScript from this example. The rest of the
root template follows:
<body onLoad=”onLoadMethod()”>
<xsl:apply-templates select=”update-results/xsql-status”/>
<h2>Price Editor Search</h2>
<form action=”price-editor-search.xsql” method=”POST”>
Search:
<input type=”text” name=”terms”>
<xsl:attribute name=”value”><xsl:value-of
select=”$terms”/></xsl:attribute>
</input>
<input type=”submit” value=”search”>
</input>
</form>
<h2>Price Search Results</h2>
<p>
<xsl:apply-templates select=”PRICE_EDITOR_QUERY”/>
</p>
</body>
</html>
</xsl:template>
Building XSQL Web Applications 433
The body has three parts. The first is xsl:apply-templates, which provides the
status of a price update. If the price-editor-search.xsql page invoked the
stylesheet, then the update-results element won’t be present in the inputted XML.
The second part of the page is the search form that feeds the price-editor
-search.xsql page. The search field has as a default value the last search term. The
final section are the results of the previous search results.
In this scheme, a query is launched against the database on the user’s first visit to
the editor per session. Because the search term is blank, the first query will always
return zero rows. However, it does seem a bit wasteful to run a query when you don’t
expect to get any data. At the same time, though, it’s redundant to create a separate sta-
tic HTML page that will basically be a copy of the stylesheet. There is an easy work-
around for this. Simply create a dummy XSQL page like the one that follows, which is
saved as price-editor.xsql.
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”price-editor.xsl”?>
<price-editor-dummy xmlns:xsql=”urn:oracle-xsql”>
</price-editor-dummy>
When called, it doesn’t even make a connection to the database. You need to make a
change in the stylesheet so that the top template will match against price-editor
-dummy:
When you call price-editor.xsql, the interface is rendered but there is no data-
base impact at all. If you want to change the look and feel of the HTML, you only have
to recode in one place.
Now it’s time to complete the rest of the stylesheet. The PRICE_EDITOR_QUERY
template is first. It sets up the table where the search results are displayed.
<xsl:template match=”PRICE_EDITOR_QUERY”>
<table>
<th>Name</th><th>Category</th><th>Price</th>
<xsl:apply-templates select=”PRICE_EDITOR_PRODUCT”/>
</table>
</xsl:template>
<xsl:template match=”PRICE_EDITOR_PRODUCT”>
<form action=”price-editor-update.xsql” method=”post” onsubmit=”return
submitForm(this)”>
<xsl:attribute name=”name”>edit_product_<xsl:value-of
434 Chapter 14
select=”PRODUCT_ID”/></xsl:attribute>
<input type=”hidden” name=”product_id”>
<xsl:attribute name=”value”><xsl:value-of
select=”PRODUCT_ID”/></xsl:attribute>
</input>
<input type=”hidden” name=”terms”>
<xsl:attribute name=”value”><xsl:value-of
select=”$terms”/></xsl:attribute>
</input>
<input type=”hidden” name=”original_price”>
<xsl:attribute name=”value”><xsl:value-of
select=”PRODUCT_PRICE”/></xsl:attribute>
</input>
<input type=”hidden” name=”product_name”>
<xsl:attribute name=”value”><xsl:value-of
select=”PRODUCT_NAME”/></xsl:attribute>
</input>
The first part of this preceding template sets up some hidden form parameters. The
first two, product-id and terms, are required by the price-editor
-update.xsql script. The others are used by the JavaScript validation scripts. The
remaining code of the template displays the name, category, and form elements as
follows:
<tr>
<td>
<xsl:value-of select=”PRODUCT_NAME”/>
</td>
<td>
<xsl:value-of select=”PRODUCT_CAT_NAME”/>
</td>
<td>
$<input type=”text” name=”new_price”>
<xsl:attribute name=”value”><xsl:value-of
select=”PRODUCT_PRICE”/></xsl:attribute>
<xsl:attribute
name=”onChange”>validatePrice(this,"<xsl:value-of
select=”PRODUCT_NAME”/>")</xsl:attribute>
</input>
</td>
<td>
<input type=”submit” value=”save”></input>
</td>
</tr>
</form>
</xsl:template>
When you search on the results, you get a form for each product returned in the
search. Each form is linked to the price-editor-update.xsql page. When you hit
Building XSQL Web Applications 435
save for the row, the product_id, terms, and new_price are passed to price
-editor-update.xsql. The stylesheet is applied to the result of price-editor
-update.xsql, which includes the status of the update itself. The last template for-
mats the status that is returned:
<xsl:template match=”xsql-status”>
<b>
<xsl:value-of select=”@rows”/> row updated for <xsl:value-of
select=”$updated_product”/>.
</b>
</xsl:template>
Your editor is complete now. There are some calls to JavaScript, but they aren’t
strictly necessary for a working editor. If someone entered an incorrect value, then
Oracle would generate an error. You can handle the error with the techniques dis-
cussed in the last section of this chapter on error handling. However, the validation
does make your editor better. It gives immediate feedback to the user about his or her
incorrect data entry. The JavaScript validation is covered in our next discussion.
<![CDATA[
<!--
var lockUI=”no”;
function lockCheck() {
if (lockUI==”yes”) {
alert(‘Updating the database. Please wait.’);
this.focus();
}
436 Chapter 14
}
function validatePrice(callingElement,product_name) {
var validChars=”0123456789.”;
valid=”yes”;
for (var i=0;i<callingElement.value.length;i++) {
if (validChars.indexOf(callingElement.value.substring(i,i+1))==-
1) {
valid=”no”;
break;
}
}
var decimalPos=callingElement.value.indexOf(“.”);
if (decimalPos!=-1 && decimalPos!=callingElement.value.length-3)
{
valid=”no”;
}
if (valid==”no”) {
alert(‘The price for ‘+product_name+’ is invalid.\n Valid
examples: 33.33, 20’);
callingElement.value=callingElement.form.original_price.value;
return false;
} else {
return true;
}
}
function submitForm(theForm) {
if (validatePrice(theForm.new_price,theForm.product_name.value))
{
lockUI=”yes”;
return true;
} else {
return false;
}
}
-->
]]>
</SCRIPT>
You could also use xsl:text to accomplish what CDATA accomplishes. In either
case, it’s important that the HTML comment strings are inside the text block. If not, the
XSLT processor will consider all of your code to be a comment and ignore all of it.
You can escape all of the special XML characters in your JavaScript on a character-
by-character basis. For instance, if you have the following line of code:
However, you probably should always have code that has these special operators
enclosed in a CDATA section. The only reason not to include the JavaScript in a
CDATA section is because you wish to perform XSLT transformations on it. Because
the special characters are operators, this means that you are trying to write your
JavaScript on the fly. This is not such a good idea. There may be a case for excluding
blocks of code because you know you won’t need them based on the input XML, but
you should definitely do that at a method level and encapsulate the entire methods
inside of templates.
A problem with this is that you can end up with awfully long method signatures,
and you can end up putting the exact same data into the output over and over. The
hidden-form parameter technique alleviates these problems. This technique was used
inside the validatePrice method to reset the value to the original price. The hidden-
form variable is set inside the template as follows:
You can then reference the value through the back door. You reference the form vari-
able of the input element that was passed in to reference the hidden variable:
callingElement.value=callingElement.form.original_price.value;
438 Chapter 14
You may not always have a form element convenient for doing this. Then, you’ll
have to reference the hidden-form parameter absolutely. If you are going to generate
multiple forms, you should name them reasonable things so that you can reference
them. We do that for the template forms:
You can also set data directly as JavaScript variables. You can do this either globally
or inside JavaScript methods. You have to break out of any CDATA section that you are
in, and then you can use any XSLT elements that you wish. Here is an example:
var numProducts=<xsl:value-of
select=”count(//PRICE_EDITOR_QUERY/PRICE_EDITOR_PRODUCT)”/>;
var firstProductName=’<xsl:value-of
select=”//PRICE_EDITOR_QUERY/PRICE_EDITOR_PRODUCT/PRODUCT_NAME[position(
)=1]”/>’;
var firstProductPrice=’<xsl:value-of
select=”//PRICE_EDITOR_QUERY/PRICE_EDITOR_PRODUCT/PRODUCT_PRICE[position
()=1]”/>’;
var productPriceArray=[
<xsl:for-each
select=”//PRICE_EDITOR_QUERY/PRICE_EDITOR_PRODUCT/PRODUCT_NAME”>
“<xsl:value-of select=”.”/>”
<xsl:if test=”position()!=last()”>,</xsl:if>
</xsl:for-each>];
<xsl:for-each select=”//PRICE_EDITOR_QUERY/PRICE_EDITOR_PRODUCT”>
var productPrice<xsl:value-of select=”PRODUCT_ID”/>=<xsl:value-of
select=”PRODUCT_PRICE”/>;
</xsl:for-each>
This last technique should be used sparingly. Though you are only setting data, you
are dynamically writing code. It can be tough to debug in such an environment. Using
hidden-form variables gives you a cleaner separation between the dynamic part of the
page and the JavaScript, which should be as static as possible. At the same time, it may
be easier overall on your application to use this technique.
Error Handling
Errors are tough to avoid. It’s rare that an application of any complexity doesn’t have
some errors at some point. The trick is to be prepared for them and handle them in a
reasonable manner. In an XSQL application, the source of errors is the actions. This sec-
tion looks at how to use XSQL’s own built-in error handling to present useful error
messages. The first section looks at how XSQL processes errors. Then, we’ll develop an
XSLT template for translating the errors into a nice format. The final section looks at
some various strategies for handling errors in your application.
Building XSQL Web Applications 439
XSQL Errors
When an XSQL action has a problem, it creates an xsql-error element. Inside the
xsql-error element is data about the error that was generated. The format is as
follows:
You can suppress the inclusion of the SQL statement by setting error-statement
to false in the action. You should get the other items regardless. The code is the Oracle
code for the error, while the action is the type of action that caused the message. The
error message is the standard message returned by Oracle that you would also get
from executing the SQL from SQL*Plus.
An error is easy to generate in our application. Just do a search on the character ‘. This
throws Oracle for a loop because this character is placed inside of a SQL statement in the
wrong place. If you comment out the stylesheet reference in the prod-search.xsql,
you should get the XML as a result of this search, as shown in Figure 14.16.
You’ll develop a search action handler in the next section to handle this kind of incor-
rect search more gracefully. For now, we’ll use it as an easy way to generate an error.
Now you just need to add the following xsl:import to all of your stylesheets:
<xsl:import href=”xsql-error.xsl”/>
Notice that you are doing an import this time, not an include. The reasoning is that
you can override the default template on a page-by-page basis. For instance, if this
error comes up in prod-search.xsl, you know that it was caused by a bad search
string. You can put the following template in prod-search.xsl to handle the error:
<xsl:template match=”xsql-error”>
Fix your broken search string!
<xsl:apply-imports/>
</xsl:template>
Building XSQL Web Applications 441
This will give the error and then invoke the template that resides in the xsql
-error.xsl stylesheet. It is also possible to give more refined messages based on the
error code that was returned. This one interprets the error based on the error code that
was returned:
<xsl:template match=”xsql-error[@code=911]”>
Hey! Don’t use single quotes in your searches. They aren’t allowed.
</xsl:template>
Because you know what the error is, you don’t need to trouble the user with all of
the error code stuff and Oracle messages. This is easily suppressed—just don’t use the
xsl:apply-imports element.
What if you want only an error page if any error occurs? You can do that by using
the following template that matches at the root of the stylesheet:
<xsl:template match=”/”>
<xsl:choose>
<xsl:when test=”count(//xsql-error)>0”>
<html>
<head></head>
<body>
<xsl:apply-templates select=”//xsql-error”/>
</body>
</html>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select=”/prod-search”/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
Moving On
This chapter brought together all of the concepts of the previous chapters. In addition
to writing XSQL pages, you also developed XSLT code and used SQL, Oracle Text, and
the Oracle XML functionality. You should be able to use this chapter as a basis for
developing your own XSQL applications. The next chapters move beyond the core of
XSQL, and show you how to use it in new ways and extend the framework. The next
two chapters show you how to use XSQL from the command line and how to use XSQL
with Web services. Then, for the last three chapters of the book, you’ll see how to use
XSQL in conjunction with Java.
CHAPTER
15
The command line utility gives you the ability to access most of the functionality of
XSQL from the command line. You pass the URL of an XSQL page and, optionally, an
output file and its parameters to the command line utility. It gives you back the results
of processing the XSQL page. Since it isn’t servlet-based, you can’t make use of session
parameters or cookies, but you can pass request-level parameters if you’d like.
The first section covers how to use the command line utility first. The next section
provides two examples to demonstrate the command line utility. One example shows
how to create a newsletter; the other, shows to send a newsletter with a simple shell
script.
The xsqlpage argument is the URL for the XSQL page that you wish to process.
You don’t have to have a Web server running to use the command line tool, though.
You can use the file:/// protocol to use local files. The out argument is an optional
argument for an output file. If you specify it, the output will be written to the file.
443
444 Chapter 15
The other arguments are parameters that you wish to pass to the XSQL page. These
arguments will function just like request parameters passed in the query string of
HTTP GETrequests. Action handlers that reference the servlet environment beyond
simple request parameters won’t work properly.
Oracle also provides a batch file so that you don’t have to type out the full class
name. It resides in the bin directory of your XSQL distribution or the bin directory of
your Oracle distribution if you installed it in conjunction with your Oracle server. You
use it as follows:
Before using the command line tool, you’ll need a XSQLConfig.xml file in the
classpath that you are running from. The classpath exists as an environment variable,
so just look at it to see where all it points to. You may also have to add the following jar
files to the classpath before the command line utility will work.
■■ jlib/sax2.jar
■■ rdbms/jlib/xsu12.jar
■■ lib/xmlparserv2.jar
■■ lib/oraclexsql.jar
■■ jdbc/lib/classes12.jar
You may be tempted to use the command line tool with XSQL pages that are part of
an online application. You probably won’t have a lot of success calling them with the
HTTP protocol, like this:
>xsql http://localhost/xsql/momnpup/home.xsql
By the time the response comes back, a stylesheet will have already been applied.
What you get back isn’t the original XSQL page but an HTML page. The command line
tool can’t make any sense out of it, so you get a weird error like this:
You won’t find any link tag on page 5 of your XSQL page. You need only call the
XSQL page with a file URL, make a copy of the XSQL page without reference to the
stylesheet, or set the xml-stylesheet parameter to none if this is allowed by your
setup.
So, what if you want the command line tool to apply a stylesheet to my results? You
do this by passing it a xml-stylesheet parameter. The command line parameter has
three such parameters, as described in Table 15.1.
Command Line Utility 445
PARAMETER MEANING
Text Example
So far, you have been very focused on developing applications for the Web. The pur-
pose of this chapter is, in part, to look beyond the Web and examine how else XSQL can
be used. In this example, you’ll use XSQL to generate a plain-text file. Instead of mak-
ing a Web page, you’ll make one of those goofy email newsletters that likes to refer to
you by name. Here’s how you do it:
Hi <xsl:value-of select=”NAME”/>,
Cheers!
</xsl:template>
</xsl:stylesheet>
446 Chapter 15
Now you need an XSQL page that will create the email for a particular email
address. Here’s one:
<?xml version=”1.0”?>
<xsql:query connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
SELECT * FROM newsletter WHERE email=’{@email}’
</xsql:query>
The following command line pulls the pieces together. It specifies that the newslet-
ter.xsl stylesheet should be used and that the email parameter should be passed.
In this example, the output writes to the console. If you would prefer to write it to a file,
you need only specify that file as the second parameter:
Script Writing
By using the XSQL file from the preceding section, you can generate a newsletter itself.
To actually send it, you just have to pass the newsletter to some kind of mail-handling
program to get it out to the right recipients. In this example, you’ll write a simple script
that can be used on Unix with the mail utility. It will send out a batch of newsletters
based on a particular XSQL page. Of course, the scripts that you can write with XSQL
are pretty unlimited. You can even write SQL scripts based on your XSQL output. This
script is mainly intended to get your imagination going about the kind of scripts that
are possible.
The first step is to figure out how to send the newsletter to a single address. This is
rather simple:
To do the mailing, you need an XSQL page that captures all the addresses in the
newsletter table. The following XSQL page will do the trick:
<?xml version=”1.0”?>
Command Line Utility 447
<xsql:query connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
SELECT email FROM newsletter
</xsql:query>
Now you need a stylesheet that creates one of the foregoing mailer commands for
each of the email addresses, such as the following:
<xsl:template match=”ROW”>
java oracle.xml.xsql.XSQLCommandLine file:///java/xsql/newsletter.xsql
xml-stylesheet=file:///java/xsql/newsletter.xsl email=<xsl:value-of
select=”EMAIL”/>|mail -t <xsl:value-of select=”EMAIL”/> -s “Newsletter”
</xsl:template>
</xsl:stylesheet>
This stylesheet creates a separate command for each newsletter. When the script runs,
a newsletter will be generated and then piped to the mail command.
#/bin/sh
The command line tool gives you a simple but powerful means of creating all kinds
of scripts. If you need data out of the database to be in your scripts, you can use XSQL
to help create the scripts. A lot of the same lessons from the earlier JavaScript discus-
sion apply. Most important, however, is not to get too fancy. If you find that you are
using stylesheets to greatly modify the logic of the stylesheet, you may be creating a
beast that no one can maintain.
448 Chapter 15
<a>
<xsl:attribute name=”href”>prod-details.xsql?product_id=<xsl:value-of
select=”PRODUCT_ID”/></xsl:attribute>
<xsl:value-of select=”PRODUCT_NAME”/>
</a>
You don’t want to link to the XSQL page, because it will query the database. Instead,
you want to link to a static page. The first step is to determine where you will keep the
static pages and how you will name them. For this example, each details page will
have a name like prod-details-1.html, where 1 is the id for the product. They’ll
live in the /momnpup virtual directory. Thus, this particular link should look like the
following:
<a>
<xsl:attribute name=”href”>/momnpup/prod-details-<xsl:value-of
select=”PRODUCT_ID”/>.html</xsl:attribute>
<xsl:value-of select=”PRODUCT_NAME”/>
</a>
The next step is to figure out how to create a single page. The following will create
the HTML for a single page:
Now you just use XSQL to generate a script for the entire site. Here is what the XSQL
looks like:
<?xml version=”1.0”?>
<xsql:query connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
SELECT id FROM product
</xsql:query>
Command Line Utility 449
Next, you need a stylesheet that will actually create the script:
<xsl:template match=”ROW”>
java oracle.xml.xsql.XSQLCommandLine file:///java/xsql/momnpup/prod-
details.xsql new/prod-details-<xsl:value-of select=”PRODUCT_ID”/> xml-
stylesheet=file:///java/xsql/newsletter.xsl product_id=<xsl:value-of
select=”PRODUCT_ID”/>
</xsl:template>
</xsl:stylesheet>
You generate the script as follows. You only have to generate the script if new prod-
ucts have been inserted into or deleted from the database since the last time that you
ran the script. If the set of products is the same, then you can use the last script that you
created.
XSQL and XSLT are useful in a command line environment. The command line tool
gives you an easy way to access the database and get data into the scripts.
Moving On
This chapter showed you how you can use XSQL as a command line tool. The XSQL
command line utility is a good way for you to easily access the database. This is only
one way that you can use XSQL beyond Web publishing. The next chapter shows how
you can create Web services with XSQL; Chapter 17 shows you how to use XSQL from
inside your Java applications.
CHAPTER
16
Web services mean different things to different people. Some will have you believe that
you need a certain server to provide Web services, while others say that they have to
involve certain protocols, such as ebXML, Simple Object Access Protocol (SOAP), or
Universal Description, Discovery, and Integration (UDDI). This chapter defines Web
services as a way to interface two applications with each other via the Web by using
XML. The first section looks at the architecture of Web services and how XSQL fits in.
Before diving into this discussion, it is important to put the subject in context with
the other beasts that roam the Web services world. The model of Web services pre-
sented here comprises very simple, easy-to-use architecture. You’ll learn how to set up
a couple of XSQL pages so that you can provide database data to other applications,
and you’ll also see how to receive information to place in your own applications.
Although it’s exciting, the SOAP standard moves far beyond the simple interchange of
data. SOAP provides a lightweight mechanism for allowing objects, such as JavaBeans
or Component Object Model (COM): objects, to interact with each other. Toward this
end, the SOAP standard provides transaction and data-type support. If you are trying
to create a true distributed application, SOAP would be a much better starting point
than the XSQL-based Web services model that you will learn here.
451
452 Chapter 16
N OT E XSQL uses DOM, which you’ll learn more about in the next chapter.
DOM requires that all the data be loaded into memory before it is sent to the
client. If you try using XSQL with Web services that transfer a lot of data, you’ll
need more memory on the server side.
SOAP does come with its own overhead and learning curve. For SOAP to be used,
both client and server applications must have SOAP processors, and if development
needs to be done, developers must be familiar with SOAP. Presented here is a simple
lightweight model that is easily accomplished. It introduces you to the Web services
fundamentals on which SOAP is based, so if you need to move on you’ll be ready.
Architecture
In the traditional world of the Web, you have a Web browser request a Web page. The
HTML page is downloaded in some manner that is pleasing to the user. In an XML-
and HTTP-based Web services model, an application makes a request of your Web
server. The application expects XML as a result. The XML should be understandable to
the requesting application. Generally, this means that it is valid in accordance with a
defined schema. The application on the other end may also send XML to be processed
by your XSQL application as well as standard HTTP parameters. Before processing the
sent XML, XSQL must have the data in the canonical schema described in Chapter 5.
The basic Web services architecture is diagrammed in Figure 16.1.
URL
<ROWSET>
<ROW>
....
</ROW>
</ROW>
....
</ROW>
</ROWSET>
XSQL
Client
<ROWSET>
<ROW>
....
</ROW>
</ROW>
....
</ROW>
</ROWSET>
XSQL
Request
Figure 16.1 Basic Web services architecture.
Web Services with XSQL 453
The Web services application probably isn’t going to want the XML data it receives
to be in the canonical Oracle schema. Likewise, it probably won’t want to post data in
that same canonical schema. This is where XSLT comes in to play. You use separate
XSLT stylesheets to transform data on the way into and on the way out of the XSQL
application. This process is diagrammed in Figure 16.2. To transform the input, you
can use a stylesheet with any of the XML handling actions documented in Chapters 5
and 7, and your custom actions can also be written so that they transform input. The
other built-in actions cannot transform or use XML input.
In both cases, you perform XML-to-XML transformations, which are covered in the
next section. The application can also send standard HTTP parameters to your XSQL
app. Your application can interpret them just as they are interpreted for Web browser-
based applications. An XSQL Web services application works very much like a tradi-
tional Web application, with the following key differences:
These points not withstanding, the lessons you’ve learned so far apply. You use the
same actions and have to make the same decisions as to when to use action handlers
and stored procedures. You should still try to modularize your XSQL and your XSLT as
much as possible. In general, developing Web services is like developing traditional
Web applications, except you’ll probably get more input in the request. You output
some particular flavor of XML rather than HTML, and the user is much stricter about
what you send.
454 Chapter 16
URL
<ROWSET>
<ROW>
....
</ROW>
</ROW>
.... xml handling action
</ROW> xml handling action
</ROWSET> custom action
custom action
HTTP built-in actions
Request XSQL
XSLT
Stylesheets
Client
<ROWSET>
<ROW>
....
</ROW>
</ROW>
....
</ROW>
</ROWSET> Output
Stylesheet
XSQL
Result
Figure 16.2 Web services with XSLT.
Now it’s time to think a bit more deeply about Web services architectures. In Chap-
ter 14, you saw how multiple pages make up an application. The same can be true of a
Web services system. The Web services consumer makes an initial call of some sort, fol-
lowed by subsequent calls. The consumer may make use of data collected in the previ-
ous calls, or maybe not. Although you may need to have a good understanding of how
the system makes use of the data it receives, you don’t have to worry with passing
parameters in the same ways that you did in Chapter 14.
In the traditional Web, you can think of a Web browser as a Web services consumer.
It makes the request, then processes the data it gets back. The difference is that you
know (more or less) how it is going to process the data. You know that if you want the
hyperlinks to come back to the application with a particular argument, you’ll have to
write the hyperlinks that way when you write out the page. You also know that you
can set cookies and thus also use session parameters as long as the user hasn’t turned
cookies off.
In the Web services world, you don’t know what the Web services consumer looks
like. But generally, you can be guaranteed that it is well tuned for the purposes of the
application. If it needs to make another request, it will probably put the request
Web Services with XSQL 455
together based on the XML data that it downloaded. You probably don’t need to put
together the hyperlinks on the server side.
Of course, as soon as you read this you’ll hear that the Web services consumer that
you are going to be working with requires exactly this. But unlike Web browsers, they
don’t have to. While Web browsers are always more or less fixed in their functionality,
Web services consumers vary greatly in how they behave. And while Web browsers are
always black boxes that you must conform to, you will often have a role in designing
and developing the Web services consumers.
Web services consumers vary. In terms of XSQL, perhaps the most important way
they vary is how they interact with the XSQL provider over a series of differing HTTP
transactions. The consumer can use either multiple URLs—one for each type of
transaction—or a single URL that multiplexes types of transactions (see Figure 16.3).
In this case, you have a different XSQL page for each type of request. In terms of
XSQL, this is really what you want. The XSQL transforms the incoming data; then it
passes it to one of the XML handling actions for processing into the database. The alter-
native is the multiplex architecture, as shown in Figure 16.4.
The idea here is that the Web services provider determines the appropriate response
by examining the XML that comes in as part of the request. The problem with this
approach is that you simply don’t get the chance to do this in the simple XSQL archi-
tecture. To make these kinds of decisions, you have to have an action handler process
the XML. In such a case, it becomes questionable what benefit you would get from
using XSQL at all.
Now that you have an understanding of how Web services architectures are put
together, the next discussion looks at how to create a simple Web services consumer.
Request Handler A
A.xsql
Request Handler B
B.xsql
Request Handler C
C.xsql
Web Services
Consumer
Request Handler A
Request Handler B
services-handler.xsql
Request Handler C
Web Services
Consumer
The consumer doesn’t accomplish any grand business objective. But you will see
how to perform, from a Java application, basic communication with a Web server by
using a main method and a couple of supporting methods. Here is the main method
and the requisite imports and class definition:
import java.io.Reader;
import java.io.Writer;
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.PrintWriter;
import java.io.InputStreamReader;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
Web Services with XSQL 457
import java.net.URL;
import java.net.URLConnection;
import java.net.MalformedURLException;
import java.net.URLEncoder;
import java.util.Hashtable;
import java.util.Enumeration;
String xmlFile=null;
URL target;
Hashtable params=new Hashtable();
if (!app.setParams(args)) {
app.writeError(errMesg);
}
System.out.println(“Opening Service”+args[0]);
app.invokeWebService(System.out);
The default constructor is used for this class. The rest of the code is in the set-
Params and invokeWebService methods. The setParams loads the first parame-
ter as the URL; it then loads the rest of the optional parameters. If the second parameter
doesn’t have an equals sign, it will be treated as a filename. The file is loaded and sent
along with the HTTP request. Here’s how the parameters are set:
try {
target=new URL(args[0]);
} catch (MalformedURLException e) {
errMesg=”Invalid URL”;
return false;
}
int paramPos=1;
while (paramPos<args.length) {
String paramStr=args[paramPos];
int eqPos=paramStr.indexOf(“=”);
String fileName=null;
String key=paramStr.substring(0,eqPos);
String val=paramStr.substring(eqPos+1,paramStr.length());
val=URLEncoder.encode(val);
params.put(key,val);
paramPos++;
}
return true;
}
The HTTP transaction takes place in the invokeWebService method. First, the
XML file is opened if an argument for one was provided. Then, the URLConnection
is instantiated and the request, including any XML, is sent to the HTTP server. A reader
for the response is created, and the data is written to the out stream.
String inputLine;
targetReader.close();
This looks like any stateless HTTP client written in Java, except for the Content
-Type setting. This setting is required when you send XML. The actual transmission
of the XML is done in setRequestData, which is given as follows. Also, the setting
sends any parameters and values along after it URL-encodes them.
Enumeration e=params.keys();
System.out.println(“params: “+params);
while (e.hasMoreElements()) {
String key=(String)e.nextElement();
String val=(String)params.get(key);
outWriter.println(key+”=”+val);
}
if (xmlReader!=null) {
There is only one method left to document—the writeError. This is a simple util-
ity method for writing out any errors. Since it is the last method, this code snippet also
contains the closing brace for the class:
Now you can put this code to use. In Chapter 7, you used a table called newsletter
to study the XML handling actions. In this example, you use the command line tool to
input some rows into the table. Here is the XSQL that will exist on the Web server side;
the filename is insert-to-newsletter-plain.xsql:
<?xml version=”1.0”?>
<page connection=”momnpup” xmlns:xsql=”urn:oracle-xsql”>
<xsql:insert-request table=”newsletter”/>
</page>
When the request arrives at the HTTP server, it looks like this:
Content-length: 226
There are two differences between this and a standard post. First, the Content
-Type is set to text/xml. The second difference is more obvious. Instead of having a
list of name-value pairs in the body of the request, you have the XML document con-
tained in newsletter.xml.
XSQL processes the data into the database and then returns the result. In this case,
the result looks like this:
Opening http://localhost/xsql/momnpup/insert-request.xsql
<?xml version = ‘1.0’?>
<page>
<xsql-status action=”xsql:insert-request” rows=”2”/>
</page>
The result isn’t transformed, so you get a plain xsql-status message. Likewise,
you had to have your input in the plain-vanilla canonical format as well. If you are
forced to deal with the XML like this, your Web services wouldn’t be very flexible. As
with the application development discussed in Chapter 14, the secret lies with XSLT.
The remaining sections focus on how you can use XSLT to create truly independent
Web services.
XML-to-XML Transformations
In most examples given in earlier chapters, the target of the transformation is HTML.
The HTTP client makes a request and the XSQL application transforms XML to HTML
by using XSLT. In this section, you’ll see how to transform into XML, which is required
by most Web services consumers. Your stylesheet starts with XML in the canonical Ora-
cle format and then transforms that into another XML document. Consequently, the
Web services consumer takes the outputted XML document. This discussion looks at
the specifics on how to perform XML transformations.
The XSLT stylesheet is referenced in the XSQL page exactly as it is in other cases. The
key syntax difference is to use the xsl:output element so that XML is sent to the
consumer.
462 Chapter 16
<xsl:output method=”xml”/>
This should be an immediate child of the root element of your stylesheet. Depending
on the requirements of your Web services consumer, you may also need to use some of
the other attributes of the xsl:output element. The most commonly used are
encoding, doctype-public, and doctype-system. All of these are covered in
Chapter 14, so here we’ll just look at them in terms of how they apply to Web services.
The encoding attribute controls what character encoding the XSLT processor will
use when it sends the output to the Web services consumer. If your Web services con-
sumer expects a particular character encoding, then you’ll need to set it in the xsl:out-
put element. Otherwise, you may encounter some very difficult-to-solve errors.
The doctype-public and doctype-system attributes concern DTDs. You
haven’t seen DTDs actually used yet because Web browsers don’t need them. But in
Web services, you will usually have some kind of schema definition. The consumer
may just assume that it always receives valid XML, but more likely it wants to have the
schema referenced in the document. The doctype-public and doctype-system
attributes take care of this when you use the DTD schema language. If you need to
have a DTD in your document that looks like the following,
“http://www.momnpup/DTD/my_def.xml”>
<xsl:output method=”xml”
doctype-public=”-//SOME_DEF//DTD MYDEF 1.1//EN”
doctype-system=” http://momnpup.com/DTD/my_def.xml”>
If you use an XML-based schema language, such as W3C’s XML Schema, you can set
the schema as you would any other XML data. In the case of XML Schema, you would
include only the following as static text in your stylesheet:
<root
xmlns=”http://momnpup/ns/my_namespace”
xmlns:xsi=”http://www.w3.org/2001/XMLSchema-instance”
xsi:schemaLocation=”http://momnpup.com/xsd file:my_def.xsd”>
From here, your stylesheet transformations will be just like the transformations that
you’ve been doing all along. Instead of conforming to HTML, you conform to the rules
of the Web consumer’s expected schema. This usually means that you make a lot of use
of the xsl:element and xsl:attribute elements.You’ll start with the simple
XSQL tricks; then you’ll look at the XSLT techniques. For this example, imagine that
the Web services consumer expects a document like the following when it requests a
list of products:
<PRODUCTS>
<PRODUCT>
Web Services with XSQL 463
<PRODUCT_ID>3</PRODUCT_ID>
<PRODUCT_NAME>Step Ladder</PRODUCT_NAME>
<PRODUCT_PRICE>30.00</PRODUCT_PRICE>
</PRODUCT>
<PRODUCT>
<PRODUCT_ID>4</PRODUCT_ID>
<PRODUCT_NAME>Coffee Mug</PRODUCT_NAME>
<PRODUCT_PRICE>20.00</PRODUCT_PRICE>
</PRODUCT>
</PRODUCTS>
SELECT id,
name,
price
FROM product
Unfortunately, this doesn’t solve the problem, because the consumer expects prod-
uct_id, product_name, and product_price elements. This can be easily
addressed just by aliasing the column names in SQL so that you get the following
XSQL page:
<xsql:query connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
SELECT id AS product_id,
name AS product_name,
price AS product_price
FROM product
</xsql:query>
<ROWSET>
- <ROW num=”1”>
<PRODUCT_ID>3</PRODUCT_ID>
<PRODUCT_NAME>Step Ladder</PRODUCT_NAME>
<PRODUCT_PRICE>30.00</PRODUCT_PRICE>
</ROW>
- <ROW num=”2”>
<PRODUCT_ID>4</PRODUCT_ID>
<PRODUCT_NAME>Coffee Mug </PRODUCT_NAME>
<PRODUCT_PRICE>20.00</PRODUCT_PRICE>
</ROW>
Now you can use the rowset-element and row-element attributes in your
XSQL to rename the rowset and row elements:
<xsql:query connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”
464 Chapter 16
rowset-element=”PRODUCTS”
row-element=”PRODUCT”>
SELECT id AS product_id,
name AS product_name,
price AS product_price
FROM product
</xsql:query>
This gives the consumer the following output, which is almost perfect:
<PRODUCTS>
- <PRODUCT num=”1”>
<PRODUCT_ID>3</PRODUCT_ID>
<PRODUCT_NAME>Step Ladder</PRODUCT_NAME>
<PRODUCT_PRICE>30.00</PRODUCT_PRICE>
</PRODUCT>
- <PRODUCT num=”2”>
<PRODUCT_ID>4</PRODUCT_ID>
<PRODUCT_NAME>Coffee Mug</PRODUCT_NAME>
<PRODUCT_PRICE>20.00</PRODUCT_PRICE>
</PRODUCT>
There is only one problem with our output. Each product has an attribute num, but
the schema requires that it be count. This can be fixed by setting the id-attribute
in the xsql:query as follows:
<xsql:query connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”
rowset-element=”PRODUCTS”
row-element=”PRODUCT”
id-attribute=”count”>
SELECT id AS product_id,
name AS product_name,
price AS product_price
FROM product
</xsql:query>
For this simple example, the output is valid according to the schema, and you
didn’t need XSLT at all. The next example requires XSLT. You need to output data that
looks like this:
<CATEGORY_LIST>
<CATEGORY id=”7”>
<NAME>Bathroom</NAME>
<DESCRIPTION>Keep clean and healthy with these products</DESCRIPTION>
<PRODUCT id=”10”>
<NAME>Band-aids</NAME>
<PRICE>5.31</PRICE>
</PRODUCT>
Web Services with XSQL 465
<PRODUCT id=”3”>
<NAME>Tweezers</NAME>
<PRICE>3.99</PRICE>
</PRODUCT>
</CATEGORY>
<CATEGORY id=”8”>
<PRODUCT id=”11”>
<NAME>Jeans</NAME>
<PRICE>25.99</PRICE>
</PRODUCT>
</CATEGORY>
This query is immediately out of scope of the XSQL tricks, because the id is the
product id, not the row number. Also, it has a tree format that isn’t reproducible with
XSQL. You can get all of the data with a single query by using cursors, but XSQL will
put a “_ROW” suffix at the end of the cursor field. Here’s the XSQL page that will get
the data:
<PRODUCT_CATEGORIES>
<CATEGORY num=”1”>
<ID>7</ID>
<NAME>Bathroom</NAME>
<DESCRIPTION>Keep clean and healthy with these products</DESCRIPTION>
<PRODUCTS>
<PRODUCTS_ROW num=”1”>
<ID>10</ID>
<NAME>Band-aids</NAME>
466 Chapter 16
<PRICE>5.31</PRICE>
</PRODUCTS_ROW>
</PRODUCTS>
</CATEGORY>
</PRODUCT_CATEGORIES>
The XSLT faces a couple of challenges. You have to rename several of the element
names, and you have to add the id attribute for the CATEGORY and PRODUCT ele-
ments. It would be possible to create a highly verbose stylesheet that has a template for
every single element. For this example, the stylesheet is much more concise. It assumes
that all the elements are in the correct order, and it makes use of the xsl:element,
xsl:attribute, and xsl:copy-of to translate the structure. The header informa-
tion and the top-level template is as follows:
<xsl:template match=”/PRODUCT_CATEGORIES”>
<CATEGORY_LIST>
<xsl:apply-templates select=”CATEGORY”/>
</CATEGORY_LIST>
</xsl:template>
The top-level template makes CATEGORY_LIST the root and then defers to the
CATEGORY template to do the rest of the work. Of course, you could change the
rowset-element to CATEGORY_LIST in the XSQL page. However, the preceding
example illustrates an important point—if the element name is essentially static, you
can always just name it in the template like this. If you don’t mind verbosity, you can
just structure the entire document with static text like this. But for this example, you
can use a cleaner approach, as follows:
<xsl:template match=”CATEGORY”>
<xsl:element name=”CATEGORY”>
<xsl:attribute name=”id”><xsl:value-of select=”ID”/></xsl:attribute>
<xsl:copy-of select=”*[name()!=’ID’ and name()!=’PRODUCTS’]”/>
<xsl:apply-templates select=”PRODUCTS/PRODUCTS_ROW”/>
</xsl:element>
</xsl:template>
The preceding template overcomes two challenges. First, it sets the id element as an
attribute of the category element. Incidentally, you can use the id-attribute and
id-attribute-column attributes of xsql:query to accomplish the same thing.
You would set id-attribute to “id” and set id-attribute-column to “ID”.
Regardless, sooner or later you will probably have to use the preceding template. If
you need to set more than one attribute, you’ll have to use this template.
Second, it copies all the values of the children into the output, except for PRODUCTS
and ID. You can use the xsl:copy-of element if the element name in the XSQL
Web Services with XSQL 467
datagram is the same as the one desired and if you want to keep all the attributes (if
there are any) and child nodes. In our case, the name in the datagram is right. This
name is more than coincidental. You can control the names of elements in the result set
by aliasing them to what you want. You can also control whether the element has child
nodes. When you work at this level of a datagram, you will not have child nodes unless
you reference a cursor, collection, or object. The PRODUCTS element does represent a
cursor, so it is handled in the following template:
<xsl:template match=”PRODUCTS/PRODUCTS_ROW”>
<xsl:element name=”PRODUCT”>
<xsl:attribute name=”id”><xsl:value-of select=”ID”/></xsl:attribute>
<xsl:copy-of select=”*[name()!=’ID’]”/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This template handles the problem with the PRODUCTS and PRODUCTS_ROW ele-
ments. You don’t need them, though you do want their children. This template creates
a new element for each of the children and effectively skips two levels in the XSQL
datagram. As before, an attribute is set by using a child value; the other elements are
then copied. The id element isn’t needed, so it is ignored in the xsl:copy-of ele-
ment. This template is the last in the stylesheet, so the closing tag is included in the
code snippet.
The two examples show a few ways how you can easily convert the XML datagram
to the correct output. You can use the XSQL tricks described here to get the output close
to if not entirely correct, and then you can greatly simplify your XSLT stylesheets by
making use of xsl:element, xsl:attribute, and xsl:copy-of.
Moving On
In this chapter you have learned about how Web services can work with XSQL. This
and the previous chapter showed you that XSQL can be very useful beyond traditional
Web pages. From the next chapter on, you’ll start learning how to extend the XSQL
framework with Java. The next chapter covers how to use XSQL from inside your
applications. In Chapter 18, you’ll see how to write your own custom action handlers
and serializers.
CHAPTER
17
So far, you’ve used XSQL as a framework. You encapsulate SQL and PL/SQL in an
XSQL file, reference an XSLT stylesheet, and produce a result. In the next chapter,
you’ll see how to extend the framework by writing your own custom action handlers.
This chapter looks at a completely different approach—how to use XSQL inside your
programs.
Oracle provides a Java API for XSQL. You can instantiate an XSQLPageRequest
object and then run it against the database. It will return the result as XML. At that
point, you can transform it by using a stylesheet. This gives you some advantages. For
example, you can keep the SQL outside of your programming logic. If you need to
change the SQL, you can do so in an external file instead of having to recompile code.
Now you have an XML document that can be used by other parts of your application.
Instead of having to process a result set and create your own XML document, this is all
done automatically. Also, you can transform the document programmatically by using
a stylesheet.
This chapter looks at how to use XSQL programmatically, starting with a
sample program. You will get an idea of what you can do with the APIs. Then the
XSQLPageRequest class and XSLTPageProcessor class are studied in detail. The
last two discussions look at the DOM and SAX APIs provided by Oracle, which give
you two ways of processing XML documents.
469
470 Chapter 17
A Sample Program
The sample program presented here runs from the command line. It loads an XSQL
file, processes it against the database, outputs the XSQL datagram to the console, and
transforms the datagram with a supplied stylesheet and outputs that as well. As with
the example code for stylesheets given in the previous chapter, this sample program is
very utilitarian. It shows you how to use some of the major functionality entry points.
It should also serve as a good foundation for the other discussions in this chapter.
The usage of the program is as follows:
The program itself consists of two methods: the main method and a parameter set-
ting method. The start of the class and the parameter setting method is as follows:
import oracle.xml.xsql.XSQLRequest;
import oracle.xml.parser.v2.XMLPrintDriver;
import oracle.xml.parser.v2.XMLDocument;
import oracle.xml.parser.v2.XMLDocumentFragment;
import oracle.xml.xsql.XSQLStylesheetProcessor;
import oracle.xml.parser.v2.XSLProcessor;
import oracle.xml.parser.v2.XSLStylesheet;
import oracle.xml.parser.v2.DOMParser;
import oracle.xml.parser.v2.XMLNode;
import oracle.xml.jaxp.JXDocumentBuilderFactory;
import org.w3c.dom.traversal.NodeIterator;
import org.w3c.dom.traversal.NodeFilter;
import org.w3c.dom.Document;
import java.util.Hashtable;
import java.io.PrintWriter;
import java.net.URL;
import java.net.MalformedURLException;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.DocumentBuilder;
import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl;
if (args.length==0) {
System.out.println(“Usage: CommandLineExample XSQLPageURL
[StylesheetURL] [Param=Val] ...”);
return false;
}
xsqlPageStr=args[0];
int paramPos=1;
if (args.length>1) {
String secondParam=args[1];
if (args[1].indexOf(“=”)==-1) {
try {
stylesheetURL=new URL(secondParam);
} catch (MalformedURLException e) {
System.out.println(“Stylesheet argument is malformed”);
}
paramPos++;
}
while (paramPos<args.length) {
String paramStr=args[paramPos];
int eqPos=paramStr.indexOf(“=”);
String key=paramStr.substring(0,eqPos);
String val=paramStr.substring(eqPos+1,paramStr.length());
params.put(key,val);
}
return true;
}
This method gathers the parameters of the command line and readies them for pro-
cessing by the main method. The real action is in the main method itself. Here, the
XSQL page is read in and instantiated as an XMLDocument. The document is then used
to create an XSQLPageRequest object, which is then processed. An XML document is
returned and printed. Then a stylesheet, if one has been provided, is applied, and the
result of the transformation is printed to the output.
This code parses the document using the JAXP method. You get a document builder
factory and then parse the results of the URL. As with the command line utility, you
have to be careful when you use an HTTP URL. If the XSQL page at that URL refer-
ences a stylesheet and the XSQL servlet is configured, you will get the transformed
result. This will probably be HTML, which won’t parse as an XML document at all.
Even if it happens to be XML, it won’t be what you want. If you use a file:// URL,
you will avoid this problem entirely.
The next few lines of code instantiate an XSQLRequest object, execute it, and prints
the results to the console. The XML document that you instantiated in the previous line
is passed to the XSQLRequest object along with the original URL. The URL is passed
so that any URLs contained in the document can be properly resolved. You can also
instantiate the XSQLRequest object based solely on the URL. The example does it this
way so that you can see how to create an XML document from a file. If you want, you
could modify the XSQL source itself before continuing with the XSQLRequest instan-
tiation.
At this point in the program, the resulting XML datagram has been printed to the
screen. There is a lot more you can do with it if you like. You can take the xsqlDoc
object and work with it through the Oracle XML DOM APIs. You can add, remove, and
modify nodes, and you can even merge nodes with other documents. You’ll see some
of this functionality in the next chapter.
The last step for the code is to apply a stylesheet if one has been specified. This func-
tionality is very simple and is more or less completely demonstrated in the following
example. This code just executes the transformation, while the real code lies in the
stylesheet itself. Here, the XSLProcessor is instantiated. Then a stylesheet object is
created and is passed to the processXSL method along with the XSQL datagram and
the output stream. In this case, the output stream is the console. Of course, you could
also pipe the results over the network or in to a string object.
if (stylesheetURL!=null) {
System.out.println(“+++ XSLT Transformation +++”);
XSLProcessor xslProc=new XSLProcessor();
XSQL Beyond Web Browsing 473
xslProc.setBaseURL(stylesheetURL);
XSLStylesheet
stylesheet=xslProc.newXSLStylesheet(stylesheetURL);
xslProc.processXSL(stylesheet,xsqlDoc,System.out);
}
}
}
This example shows you how the pieces can fit together in a simple application. The
next section delves deeper into the XSQL classes used programmatically. You will use
these same classes when, in Chapters 18 and 19, you develop custom action handlers
and serializers.
In the preceding example, you processed the XSQL request and got an XMLDocu-
ment object in return. This is convenient if you wish to do further processing on the
result. You can use the DOM APIs that you’ll learn about later. But again, you have
flexibility in how to deal with the results. If you don’t need to process the XML,
you can have the XSQLRequest class write out the results directly to a stream. The
following statement writes the results directly to the console when you execute from
the command line:
req.process(System.out,System.err);
474 Chapter 17
Almost every XSQL page that you’ve developed so far in the book has parameters.
You need a way to pass parameters to the XSQL page that you wish to process. There
are two ways to do this. First, both the processToXml() and process() methods
allow you to pass a dictionary object. The keys and values inside the dictionary object
are used as the parameter and parameter values inside the XSQL page. You used this
method, with the dictionary subclass Hashtable, in the earlier example. In its sim-
plest form, it looks like this:
Then, you pass the parameters to either the process() or the processToXML()
method:
req.process(table,System.out,System.err);
XMLDocument xDoc=req.processToXML(table);
As with the command line example, you can perform a transformation on the results.
Just use the XSLStylesheet and XSLProcessor classes. You can also manipulate the
data in any other manner that you like.
Document w3cDoc=req.processToXML();
XMLDocument oraDoc=(XMLDocument)w3cDoc;
Document w3cDoc2=oraDoc;
Element w3cElem=w3cDoc.getDocumentElement();
XMLElement oraElem=(XMLElement)w3cElem;
XMLNode n=(XMLNode)w3cDoc.getDocumentElement();
One of the most useful aspects of the Oracle DOM is the integration with XPath.
The XMLNode class has two sets of methods that will take XPath expressions as argu-
ments—selectNodes() and valueOf(). The selectNodes methods return lists
of nodes, while valueOf retrieves text values. This greatly simplifies the process of
navigating a DOM tree.
There are a variety of other methods in the Oracle DOM API that are worth explor-
ing. For instance, you can also apply a stylesheet to just a particular node by using
the transformNode() method. This has a variety of uses. Also, you can import a
node from one document in to another with just one method call—importNode() in
XMLDocument.
XSQL Beyond Web Browsing 477
Moving On
You learned in this chapter how to embed XSQL into your own applications. Even if
you never actually embed XSQL in this manner, you probably have gained a lot of
insight in how XSQL works. In general, you can find XSQL very useful in conjunction
with your application code. You can configure the SQL and the presentation very eas-
ily, because the XSQL pages and the XSLT stylesheets are outside of your code. In the
following chapters, you’ll see how to extend XSQL with action handlers and serializ-
ers. You can use these in conjunction with XSQL that is invoked programmatically.
CHAPTER
18
The time has finally come! You’ve been using the built-in action handlers throughout
the book; now it is time to make your own. As you’ve learned, custom action handlers
are a valuable piece of the XSQL puzzle. You use them to extend the base functionality
of XSQL in almost any direction that you wish.
The chapter starts with a simple action handler. You start with action handlers and
get your environment set up. Next, the APIs that you use for developing action han-
dlers are examined. You’ll learn what functionality is available and how to use it. Then
you’ll see how your action handlers can interact with the database. Examples are given
of how to use built-in actions such as xsql:query. The chapter ends by looking at
parameters and input. You’ll see how to receive and set parameters, handle input from
the XSQL page, and pass objects to other action handlers.
Getting Started
Action handlers are simple to write once you understand the subtleties of their posi-
tion in the XSQL architecture. In this section, you get your feet wet. The first step is to
get a simple action handler working. Then you’ll see how to add XML to the datagram.
These two discussions should give you a good, basic understanding of how action han-
dlers work. The next discussion compares action handler development to servlet
479
480 Chapter 18
development. They are both similar in a lot of ways yet have some important differ-
ences. Hopefully this discussion will solidify your understanding of the basic art and
science of action handler development. The last section discusses how to debug action
handlers.
The name must be the fully qualified class name. For these simple examples, the
classes aren’t in a package. If your classes are in packages, you will need to specify the
fully qualified name as follows:
In this particular example, there are no parameters, and the xsql:action element
has no children. This particular action handler doesn’t need them. If there were any
present, they would be ignored. But the xsql:action element can have parameters
and values. You’ll see later in this chapter how to manipulate them from inside the
action handler.
Before moving on to the action handler code itself, it’s worth noting that the
xsql:action element doesn’t have to be a child element. It can be a top-level element
just as any of the other actions can, as shown in the following code. You may have also
noticed that there is no database connection specified. For the simple examples given
in this chapter, you won’t need a database connection. The “Database Interaction” sec-
tion talks about connecting to the database from an action handler.
Now it’s time to write some Java code. The following is the HelloActionHandler
class. A Java class is an action handler if it implements the XSQLActionHandler
interface.
import org.w3c.dom.Node;
import oracle.xml.xsql.XSQLActionHandlerImpl;
import oracle.xml.xsql.XSQLActionHandlerImpl;
import oracle.xml.parser.v2.XMLDocument;
import oracle.xml.parser.v2.XMLElement;
import oracle.xml.parser.v2.XMLText;
import org.w3c.dom.Node;
XMLDocument doc=(XMLDocument)result.getOwnerDocument();
XMLElement elem=(XMLElement)doc.createElement(“hello”);
elem.setAttribute(“attr1”,”val1”);
XMLText tNode=(XMLText)doc.createTextNode(“hello!”);
Custom Action Handlers 483
elem.appendChild(tNode);
result.appendChild(elem);
}
}
In this example, the Oracle DOM classes are used. This is consistent with the dis-
cussion in the last chapter. You got some more functionality by using the Oracle
classes, and can always interoperate with the base DOM interfaces.
The code itself is fairly straightforward. The node result, which is passed to the
handleAction method, is the parent of the xsql:action element that invokes this
action handler. If you want to provide XML back to the datagram, you will need to
append it to the result node. To create elements and other node types, you will need to
use the appropriate create method of the owner document. You can get a handle to the
owner document through the getOwnerDocument() method of the node class.
The next example expands on the following one. This time, you create the same ele-
ment as the preceding one and then add a couple of child elements. Since the first ele-
ment has an attribute, you will need to create it from scratch. The second one doesn’t
have an attribute, so you can use the addResultElement() convenience method.
XMLDocument doc=(XMLDocument)result.getOwnerDocument();
XMLElement elem=(XMLElement)doc.createElement(“hello”);
result.appendChild(elem);
elem.setAttribute(“attr1”,”val1”);
XMLText tNode=(XMLText)doc.createTextNode(“hello!”);
elem.appendChild(tNode);
XMLElement child1=(XMLElement)doc.createElement(“child”);
elem.appendChild(child1);
child1.setAttribute(“attr2”,”val2”);
tNode=(XMLText)doc.createTextNode(“A child”);
child1.appendChild(tNode);
addResultElement(elem,”child”,”another child”);
You can continue in this way to create as many elements as you want. You’ll proba-
bly never write an action handler that doesn’t output at least one element to the data-
gram. A lot of the examples in the rest of the chapter show you different ways to output
elements. You won’t always have to do it as manually as it was done in the previous
example. As you’ll see, you can use the built-in action handlers to ease this process.
484 Chapter 18
Before moving on, it’s important to note some practices that should be avoided.
First, you’ll notice that you can access the entire datagram. This means that you could
append elements anywhere you like. The following code, for instance, appends an ele-
ment off the document root:
XMLDocument doc=(XMLDocument)result.getOwnerDocument();
XMLElement docElem=(XMLElement)doc.getDocumentElement();
addResultElement(docElem,”bad”,”bad text”);
}
This is a bad idea. Instead, you should append only to the result node that is passed to
the handleAction method. Once you start traversing the document in this manner,
you open the potential for all kinds of conflicts. You may interfere with other action
handlers and can make XSLT development very hard. Going beyond the result node to
read data is likewise risky. You are making assumptions about the rest of the datagram
that may or may not be true. It’s best to operate within the confines of the result node
that is passed to you. In fact, attempts to access siblings and the parent directly through
the node methods will result in null pointer exceptions.
Another no-no is appending more than one element to the result node. In the fol-
lowing example, you add two elements to the result node:
addResultElement(result,”bad1”,”text”);
addResultElement(result,”bad2”,”text”);
}
This code will work. It will even produce valid XML, as long the xsql:action
action that invokes it has a parent. But what if the xsql:action is the root element of
your document, as with this XSQL page?
This will result in an invalid XML document. An XML document can have only one
root element, but the action element creates two root elements. Even if you decide that
you are willing to live with an action handler that can never be a root element, you will
still make writing XSLT stylesheets difficult. It is simpler to have a single element at the
top of all XML elements that your action handler adds to the datagram. Your stylesheet
will have an easier time finding the elements you add if they are kept under a single
root element.
development to servlet development. The two are similar yet different. Since servlet
development is a model that you probably understand pretty well, you can use your
servlet experience as a touchstone.
The first thing to note is that action handlers aren’t strictly dependent on servlets.
Though all the action handler examples in this section will be invoked through XSQL
servlet-loaded pages, you can also load pages programmatically or with the XSQL
command line utility. Servlets, on the other hand, are almost always invoked in
response to an HTTP request.
Most of your action handlers will be invoked as part of an HTTP request. When they
are, you can access all the servlet information. You’ll learn in this chapter how to access
the ServletResponse, ServletRequest, and ServletContext classes. So an
action handler used from the Web has the same level of access as a servlet. In addition,
it also has access to information from the invoking XSQL page.
Where action handlers differ decidedly from servlets is on the output side. A servlet
can output whatever it wants, whereas an action handler can output only XML. While
a servlet has full control of writing the output, the only thing an action handler can do
is append XML to the node passed to handleAction. An action handler doesn’t have
control over when the data is written or what happens with the data.
If you’re an experienced servlet writer, you’ll find that you’re able to leverage a lot
of your experience. You should also find that your action handlers are more modular
than the servlet code. You can easily swap action handlers in and out of different XSQL
pages. Also, you don’t have to deal with a lot of the messier aspects of outputting for
the Web. You just push XML out and let the stylesheet take it from there.
XSQLActionHandler Interface
The XSQLActionHandler interface defines what an action handler is. You’ll get an
error if you specify a class in xsql:action that doesn’t implement this interface. As
mentioned before, the XSQLActionHandlerImpl base class implements this inter-
face, so you don’t have to worry about specifically implementing the interface. Never-
theless, it’s important for you to know exactly what is defined in this interface. The two
methods are described in Table 18.1.
486 Chapter 18
METHOD DESCRIPTION
public void init Called to initialize the action handler. The method
(XSQLPageRequest env, is always called previously to handleAction and
Element e) is called each time the action handler is invoked.
The env argument is a handle to the current
XSQLPageRequest and e is the element that is
invoking the action handler.
import oracle.xml.xsql.XSQLActionHandlerImpl;
import oracle.xml.xsql.XSQLActionHandler;
import oracle.xml.xsql.XSQLPageRequest;
import oracle.xml.parser.v2.XMLDocument;
import oracle.xml.parser.v2.XMLElement;
import oracle.xml.parser.v2.XMLText;
import oracle.xml.parser.v2.XSLException;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
XSQLPageRequest pageRequest;
XMLElement actionElem;
The init method in this case just saves the values to instance variables. They’ll be
referenced later in the handleAction method. You could also choose at this time to
call other methods and do some of the work of the action handler.
Custom Action Handlers 487
XMLDocument doc=(XMLDocument)result.getOwnerDocument();
XMLElement resultRoot=(XMLElement)doc.createElement(“result-root”);
result.appendChild(resultRoot);
The preceding block of code creates the resultRoot element to which the rest of the
elements will attach. You’ll see these lines of code throughout the rest of the chapter.
The code below pulls the text of the xsql:action element on the XSQL page and
writes it to the datagram.
try {
String actionElemStr=actionElem.valueOf(“.”);
actionElemStr=(actionElemStr.length()>0:actionElemStr:””);
XMLElement actionElemVal=(XMLElement)doc.createElement(“action-val”);
resultRoot.appendChild(actionElemVal);
XMLText tNode=(XMLText)doc.createTextNode(actionElemStr);
actionElemVal.appendChild(tNode);
} catch (XSLException e) {
// exception handling code
}
This section makes use of the XSQLPageRequest object to get the user agent
string, which is also added to the output.
String userAgentStr=pageRequest.getUserAgent();
XMLElement userAgent=(XMLElement)doc.createElement(“user-agent”);
resultRoot.appendChild(userAgent);
XMLText tNode=(XMLText)doc.createTextNode(userAgentStr);
userAgent.appendChild(tNode);
}
}
In the previous example, the init method was used to capture the XSQL-
PageRequest and action element objects. If you didn’t capture those objects in the
init method, you wouldn’t be able to do anything with them. You don’t face the same
requirement when subclassing XSQLActionHandlerImpl. There are two conve-
nience methods that make these objects available to you from the handleAction
method:
XSQLPageRequest pageRequest=getPageRequest();
XMLElement userAgent=(XMLElement)doc.createElement(“user-agent”);
Element xsqlAction=getActionElement();
//Other code
METHOD DESCRIPTION
METHOD DESCRIPTION
METHOD DESCRIPTION
METHOD DESCRIPTION
Table 18.5 describes a set of methods that helps you write error messages. Error con-
ditions may arise in your custom action handler. If they do, you want to be able to com-
municate them by using the standard error format. By using the standard format, you
can reuse the same error handling stylesheets that you use for the XSQL built-in
actions.
METHOD DESCRIPTION
XSQLPageRequest
The XSQLPageRequest object gives an action handler access to a large range of func-
tionality. You access a XSQLPageRequest object in one of two ways. First, you
can grab it in the init() method of the action handler. The other way is to call
getPageRequest() of XSQLActionHandlerImpl. Of course, the second way
works only if your action handler subclasses XSQLActionHandlerImpl.
Once you have an XSQLPageRequest object, you can use it for a variety of pur-
poses. Table 18.6 outlines a few of the more important methods.
You’ll be using these methods throughout the rest of the chapter. The XSQL-
PageRequest class is the gateway to a lot of interesting functionality.
METHOD DESCRIPTION
Before attempting to use servlet functionality, you first need to verify that you are
being invoked from a servlet. Once you know that you are operating within a servlet,
you can cast your XSQLPageRequest object to an XSQLServletPageRequest
object. The following code shows how you can acquire all of the key servlet objects.
Once you have those objects, you can do most anything you want—except writing out-
put directly.
import oracle.xml.xsql.XSQLActionHandlerImpl;
import oracle.xml.xsql.XSQLPageRequest;
import oracle.xml.xsql.XSQLServletPageRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpSession;
import org.w3c.dom.Node;
XSQLPageRequest pageRequest=getPageRequest();
if (pageRequest.getRequestType().equals(“Servlet”)) {
XSQLServletPageRequest xspr=(XSQLServletPageRequest)pageRequest;
HttpServletRequest req=xspr.getHttpServletRequest();
HttpServletResponse resp=xspr.getHttpServletResponse();
ServletContext ctx=xspr.getServletContext();
HttpSession sess=req.getSession();
String cookieVal=xspr.getCookie(“some-cookie”);
xspr.setCookie(“new-cookie”,”value”,null,null, null,true);
}
}
}
The last two lines of the code example show you two of the convenience methods
that the XSQLServletPageRequest class provides. There are a few others that are
described in Table 18.7. These are above and beyond the methods of the XSQL-
PageRequest class. These methods are covered in the last chapter.
The XSQLServletPageRequest class is your gateway to all of the servlet func-
tionality. You may even find that you are able to reuse other code that works with the
ServletRequest and ServletResponse objects. Of course, you can also set objects
onto the session for use by other action handler invocations. As long as you are careful
not to write output directly, you can create some powerful action handlers with a lot of
the power of servlets.
Database Interaction
The discussions so far have provided a good background for how action handlers work.
But you probably want your action handlers to access the database. This section covers
how to reach the database effectively. Of course, you can always open a new JDBC con-
nection. But it is far easier to reuse the built-in action handlers. It takes care of format-
ting the result as XML, and it is easy to merge the results into the datagram. If you have
to use JDBC, you should use a connection provided by XSQL. Connection pooling is
already taken care of. Each of these methodologies are discussed in this section.
METHOD DESCRIPTION
ACTION CLASS
<xsql:query> oracle.xml.xsql.actions.
XSQLQueryHandler
<xsql:dml> oracle.xml.xsql.actions.
XSQLDMLHandler
<xsql:set-stylesheet-param> oracle.xml.xsql.actions.
XSQLStylesheetParameterHandler
<xsql:insert-request> oracle.xml.xsql.actions.
XSQLInsertRequestHandler
<xsql:update-request> oracle.xml.xsql.actions.
XSQLUpdateRequestHandler
<xsql:delete-request> oracle.xml.xsql.actions.
XSQLDeleteRequestHandler
<xsql:include-request-params/> oracle.xml.xsql.actions.
XSQLIncludeRequestHandler
<xsql:include-xsql> oracle.xml.xsql.actions.
XSQLIncludeXSQLHandler
<xsql:include-owa> oracle.xml.xsql.actions.
XSQLIncludeOWAHandler
<xsql:action> oracle.xml.xsql.actions.
XSQLExtensionActionHandler
<xsql:ref-cursor-function> oracle.xml.xsql.actions.
XSQLRefCursorFunctionHandler
<xsql:include-param> oracle.xml.xsql.actions.
XSQLGetParameterHandler
Custom Action Handlers 495
ACTION CLASS
<xsql:set-session-param> oracle.xml.xsql.actions.
XSQLSetSessionParamHandler
<xsql:set-page-param> oracle.xml.xsql.actions.
XSQLSetPageParamHandler
<xsql:set-cookie> oracle.xml.xsql.actions.
XSQLSetCookieHandler
<xsql:insert-param> oracle.xml.xsql.actions.
XSQLInsertParameterHandler
import oracle.xml.xsql.XSQLActionHandlerImpl;
import oracle.xml.xsql.XSQLActionHandler;
import oracle.xml.xsql.XSQLPageRequest;
import oracle.xml.xsql.actions.XSQLQueryHandler;
import org.w3c.dom.Node;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import java.sql.SQLException;
XSQLActionHandler queryAction;
}
496 Chapter 18
The preceding init method instantiates the XSQLQueryHandler object and calls
the init() method. The queryAction variable is actually of the XSQLActionHan-
dler type. You don’t need to call any of the methods besides those defined in the
XSQLActionHandler interface, so there isn’t much of a reason to have an XSQL-
QueryHandler variable.
You don’t have to instantiate and initialize an action handler in the init method. If
you would like, you can configure the action handler in the handleAction method.
Just use the getPageRequest() and getActionElement() methods of the
XSQLActionHandleImpl to grab the arguments that you need to pass to init().
try {
queryAction.handleAction(resultFrag);
} catch (SQLException e) {
reportError(result,e.getMessage());
}
result.appendChild(resultFrag);
}
}
When you initialize the queryAction object with the xsql:action element, it
treats it just as if it were an xsql:query object. You can even set parameters
xsql:query parameters on the xsql:action element, as in the following example:
row-element=”PRODUCT”>
SELECT id,name FROM product where category_id={@category_id}
</xsql:action>
This results in the output shown in Figure 18.2. When one of the built-in action han-
dlers is passed an element via the init() method, it doesn’t care what the element
name is. In the case of XSQLQueryHandler, it looks for all of the parameters that are
allowed in the xsql:query element and treats the value as a SQL statement. It also
automatically substitutes parameter values for any parameters it finds. The same is
true for all the other built-in action handlers.
You gain great flexibility in your action handlers because they ignore the name. As
you’ll see in the “Parameters and Input” section later in this chapter, you can even pass
non-xsql elements to a built-in action handler object and get the expected results. This
next example demonstrates this flexibility in a largely theoretical way—you probably
won’t ever find any reason to do this in a real application. But it does prove beyond a
shadow of a doubt that the built-in action handlers don’t pay any attention to the name
of the element that invokes the action handler. Here, the action handler passes the
same element to two different built-in action handlers: XSQLQueryHandler and
XSQLSetPageParamHandler.
import oracle.xml.xsql.XSQLActionHandlerImpl;
import oracle.xml.xsql.XSQLActionHandler;
import oracle.xml.xsql.XSQLPageRequest;
import oracle.xml.xsql.actions.XSQLQueryHandler;
import oracle.xml.xsql.actions.XSQLSetPageParamHandler;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import java.sql.SQLException;
XSQLActionHandler queryAction;
XSQLSetPageParamHandler paramAction;
super.init(req,action);
Both init() methods are passed the exact same arguments. The queryAction
object considers the action element to be an xsql:query element, while the
paramAction element considers the action element to be an xsql:set-page-param
element.
Document doc=result.getOwnerDocument();
DocumentFragment queryActionFrag=doc.createDocumentFragment();
DocumentFragment paramActionFrag=doc.createDocumentFragment();
try {
queryAction.handleAction(queryActionFrag);
paramAction.handleAction(paramActionFrag);
} catch (SQLException e) {
reportError(result,e.getMessage());
}
result.appendChild(queryActionFrag);
}
}
Figure 18.2 Results of multiple actions that use the same action element.
Before moving on from this discussion, it’s worth noting that the action element
doesn’t have to be defined in the XSQL page. If you wish, you can construct an action
element inside your code. The following example initializes the XSQLQueryHandler
with an element that is created at runtime.
import oracle.xml.xsql.XSQLActionHandlerImpl;
import oracle.xml.xsql.XSQLActionHandler;
import oracle.xml.xsql.XSQLPageRequest;
import oracle.xml.xsql.actions.XSQLQueryHandler;
import oracle.xml.xsql.actions.XSQLSetPageParamHandler;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.Text;
import java.sql.SQLException;
XSQLActionHandler queryAction;
500 Chapter 18
Document doc=result.getOwnerDocument();
DocumentFragment queryActionFrag=doc.createDocumentFragment();
XSQLPageRequest pageRequest=getPageRequest();
Element queryElem=doc.createElement(“my-special-query”);
Text queryStr=doc.createTextNode(“SELECT name
FROM product”);
queryElem.appendChild(queryStr);
queryAction=new XSQLQueryHandler();
queryAction.init(pageRequest,queryElem);
try {
queryAction.handleAction(queryActionFrag);
} catch (SQLException e) {
reportError(result,e.getMessage());
}
result.appendChild(queryActionFrag);
}
}
In this case, you create the element named my-special-query and hard-code the
SQL that you wish to pass. The result will be all of the names for all of the products.
Since you don’t use the action element at all, you can invoke the action handler with
the following simple XSQL:
<xsql:action handler=”NoXsqlQuery”
xmlns:xsql=”urn:oracle-xsql”
connection=”momnpup”/>
Hopefully, this discussion has given you a firm grasp of the various ways that you
can reuse the built-in action handlers within your own action handlers. The process is
simple and flexible. You’ve seen how to call multiple action handlers and even call
action handlers that don’t have their own element in the XSQL page. You’ll see more
use of built-in action handlers as the chapter progresses.
JDBC Connections
In the previous discussion, you used built-in action handlers to work with this the
database. This approach is very easy if you wish to attach the result to the datagram. It
hides the complexities of JDBC entirely. But there are many good reasons that you
Custom Action Handlers 501
might need to use JDBC directly. If you wish to use the results in an intermediate step
prior to returning data, then it may be easier to use a JDBC connection. This discussion
examines the best way to do this.
The following class uses the page’s JDBC connection to execute a simple SQL state-
ment. The connection is named in the XSQL page and is already part of the JDBC con-
nection pool. This is much easier than creating a JDBC connection from scratch and
having to deal with connection pooling issues yourself. It is also easier to configure.
The invoking XSQL page specifies the connection name and the details are kept in the
XSQLConfig.xml file.
import oracle.xml.xsql.XSQLActionHandlerImpl;
import oracle.xml.xsql.XSQLActionHandler;
import oracle.xml.xsql.XSQLPageRequest;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import org.w3c.dom.Document;
import org.w3c.dom.Text;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Connection;
import java.sql.SQLException;
Connection conn;
super.init(req,action);
conn=req.getJDBCConnection();
You grab the connection from the XSQLPageRequest object. You don’t have to do
this in the init method, but it is a convenient place to do it. The connection will be
null if there is no connection specified in the XSQL page. When you go to use the con-
nection, you need to check to see it is null and generate an appropriate error message
if it is.
Document doc=result.getOwnerDocument();
Element resultRoot=doc.createElement(“result-root”);
if (conn!=null) {
try {
502 Chapter 18
while (resultSet.next()){
String val=resultSet.getString(“name”);
Element nameElem=doc.createElement(“NAME”);
Text tNode=doc.createTextNode(val);
nameElem.appendChild(tNode);
resultRoot.appendChild(nameElem);
resultSet.next();
}
result.appendChild(resultRoot);
} catch (SQLException e) {
reportError(result,e.getMessage());
}
} else {
reportError(result,”No Database Connection”);
}
}
}
The handleAction method creates a simple statement, executes it, and grabs the
result. Once you have the connection object, the full power of JDBC will be at your fin-
gertips. You can do whatever you need to do. What may be new is building XML out
of the result set data. This is a simple example in which an element is created for each
name returned in the query. In the following text, you’ll see how you can avoid this
exercise entirely by using the XSU classes.
XMLDocument doc=(XMLDocument)result.getOwnerDocument();
Element resultRoot=doc.createElement(“result-root”);
if (conn!=null) {
Custom Action Handlers 503
try {
} catch (Exception e) {
reportError(result,e.getMessage());
}
} else {
reportError(result,”No Database Connection”);
}
}
The result of this query is exactly the same as a plain xsql:query action with the
same SQL query. Each row is contained in a ROW element, and all of the ROW elements
are contained in a ROWSET element. If you wish, you can set the name for the rowset
element and the row element just as you can with xsql:query action. All of the other
options of the xsql:query action are available. The same is true for OracleXMLSave.
This example also contains a good example of when Oracle’s XMLDocument class
comes in handy. When you go to merge the documents, you can easily do so by using
the adoptNode method of XMLDocument. Merging the document strictly through
DOM is quite a bit harder.
XMLDocument doc=(XMLDocument)result.getOwnerDocument();
Element resultRoot=doc.createElement(“product-set”);
if (conn!=null) {
try {
504 Chapter 18
while (resultSet.next()){
String xmlStr=resultSet.getString(“product_xml”);
InputSource xmlIn=new InputSource(new StringReader(xmlStr));
result.appendChild(resultRoot);
} catch (Exception e) {
reportError(result,e.getMessage());
}
} else {
reportError(result,”No Database Connection”);
}
}
The query is executed as in the straight JDBC example earlier. But the value can’t be
added to the datagram as a string. All of the special characters would be escaped and
you wouldn’t be able to use transformations against the XML. Instead, you parse the
string into an XML document and then merge the two documents.
xsql:action element. Now, you’ll see how to handle parameters in your own action
handlers.
As you read this section, you’ll see that XSQL gives you many ways to control your
custom action handlers. You already know how to access all the information of a
servlet; you’re about to learn how to get input from the XSQL page and the XSQLCon-
fig.xml, also. But that’s only half the story. You can use both the XSQL parameters
and the stylesheet parameters as a powerful output mechanism. By setting a parame-
ter on a page, your action handler can communicate data with other action handlers.
Now, it’s time to conquer parameters and input! This subject material might not
seem as important as accessing the database and pushing data to the datagram. But
here you understand how to control and modularize your action handlers and how
your action handler can control other action handler’s behavior. Just think: You could
write the next xsql:query!
<xsql:action handler=”XmlTypeHandler”/>
That’s no good. If you want to use different SQL, you’ll have to recode your action han-
dler. One of the key benefits of XSQL is that you can keep the SQL statements outside of
your compiled code. For something as generically applicable as the XMLTypeHandler,
you want to be able to do something like the following:
The goal is that you can read the SQL statement in just as any other action handler
can. The XSQL page author can also set the name of the root element that will be
returned. Of course, you also want to be able to use XSQL parameters in the action ele-
ment. You’ll learn how to substitute parameter values in the next section. For this dis-
cussion, the focus is on accessing the values.
506 Chapter 18
Your first step is to modify the init() method so that you grab the action element
and the XSQLPageRequest object. Note that this time you cast the action element to
an XSQLElement variable. You’ll see why in a moment.
Connection conn;
XMLElement actionElem;
XSQLPageRequest pageRequest;
super.init(req,action);
this.actionElem=(XMLElement)action;
this.pageRequest=req;
this.conn=req.getJDBCConnection();
Now that you have the element captured, you’ll want to grab the text value of the
action element and the root-element-name attribute value. Here’s how you do it:
try {
String sqlStr=actionElem.valueOf(“.”);
String resultRootName=getAttribute(“root-element-name”,actionElem);
resultRootName=(resultRootName==null || resultRootName.length()==0?
“xml-doc-set”:resultRootName);
From here, the code is like before. The difference is that now you are working with
an SQL statement and resultRootName that are derived from the invoking XSQL
page.
XMLDocument doc=(XMLDocument)result.getOwnerDocument();
Element resultRoot=doc.createElement(resultRootName);
if (conn!=null) {
Statement sql=conn.createStatement();
ResultSet resultSet=sql.executeQuery(sqlStr);
while (resultSet.next()){
String xmlStr=resultSet.getString(“product_xml”);
InputSource xmlIn=new InputSource(new StringReader(xmlStr));
}
}
In this example, you grabbed the text value of the action element. If your action
element has child elements, you can certainly grab the values of those as well. For
instance, if the name of a child element is “child”, you can do the following to grab the
value:
String s=actionElem.valueOf(“child”);
You’ll learn more about creating nested action elements in the section after this next
one. Now it’s time to complete this example by making the preceding example param-
eter ready.
<xsql:action handler=”XmlTypeHandler”
root-name=”product-set”>
SELECT name,
p.doc.extract(‘{@xPath-exp}’).getStringVal() AS product_xml
FROM product p
WHERE p.id={@product_id}
</xsql:action>
508 Chapter 18
Oracle provides a couple of ways to substitute the parameters easily. For attributes,
all you have to do is call the getAttributeAllowingParam() method. If there is no
parameter set, you’ll need to use a line like the second one to set the default value.
String resultRootName=getAttributeAllowingParam(“root-name”,actionElem);
resultRootName=(resultRootName==null || resultRootName.length()==0?
“xml-doc-set”:resultRootName);
String rootNameStr=getAttribute(“root-name”,actionElem);
String rootNameVal=XSQLUtil.resolveParams(rootNameStr,
pageRequest);
rootNameVal=(rootNameVal==null || rootNameVal.length()==0?
“xml-doc-set”:rootNameVal);
This takes care of attributes. The next step is to handle the text of an element. Again,
there are two ways to do this. If you are interested in resolving just the text value of
the action element, you can do that with the XSQLActionHandlerImpl method
getActionElementContent(). normalizes the text and returns a string with all
parameters already substituted.
String s=getActionElementContent();
This method works only if you want the immediate child text of an element. What if
the action handler has children and you want to access their text? Then you can use the
XSQLUtil.resolveParams() method after grabbing the text.
XMLElement actionElem=(XMLElement)getActionElement();
String s=actionElem.valueOf(“child”);
String s=XSQLUtil.resolveParams(s, getPageRequest());
The following example shows you how to recursively descend an element, resolving
all of the parameters. If you want children for your action handler elements, you’ll
need something like this. It takes a pure DOM approach.
NamedNodeMap attribs=newElem.getAttributes();
Node attrib=attribs.item(i);
String valStr=attrib.getNodeValue();
valStr=XSQLUtil.resolveParams(valStr,pageRequest);
attrib.setNodeValue(valStr);
}
Custom Action Handlers 509
The preceding code resolves any attributes that the element may have. The next set of
code works through all the children nodes. If they are text, the parameters will be
resolved.
NodeList list=newElem.getChildNodes();
for (int i=0;i<list.getLength();i++) {
Node n=list.item(i);
// Text node
if (n.getNodeType()==Node.TEXT_NODE) {
log.println(“found text node”);
String valStr=n.getNodeValue();
log.println(“valStr==”+valStr);
valStr=XSQLUtil.resolveParams(valStr,pageRequest);
log.println(“valStr replaced ==”+valStr);
n.setNodeValue(valStr);
}
The last two lines take care of the recursion. If the element has a child element, the
method is called again on the child. The recursion stops and the method halts when no
more children are encountered.
if (n.getNodeType()==Node.ELEMENT_NODE) {
resolveElementParams((Element)n,pageRequest);
}
}
}
This method gives you more flexibility when designing action handlers. For
instance, you can invoke an action handler with this method. The hypothetical case is
that your action handler chooses what action to take based on the test attribute. These
could result in different SQL queries or maybe calls to other data sources. The problem
solved is one of complexity. If you need to pass a lot of information to an action han-
dler, why not use XML to help you keep it organized? This can be a more sane
approach than having to parse text inside your action handlers.
<xsql:action handler=”SomeActionHandler”>
<firstChoice test=”{@test1}”>{@valParam1}</oneChoice>
<secondChoice test=”{@test2}”>{@valParam2}</secondChoice>
<thirdChoice test=”{@test3}”>{@valParam3}</thirdChoice>
<otherwise>{@valParam4}</otherwise>
</xsql:action>
In the Java code, you clone the action element first, then the resolveElement-
Params:
Element elem=getActionElement().cloneNode(true);
resolveElementParams(elem,pageRequest);
510 Chapter 18
Having nested xsql:action elements like the one described previously does come at
a price. The action handler will have to parse the nested action handlers on each call. If
they are deeply nested, then you are adding that much more work for every action
handler call.
import oracle.xml.xsql.XSQLActionHandlerImpl;
import oracle.xml.xsql.XSQLServletPageRequest;
import oracle.xml.xsql.XSQLPageRequest;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import javax.servlet.http.HttpServletRequest;
Element actionElement=getActionElement();
if (getPageRequest().getRequestType().equals(“Servlet”)) {
XSQLServletPageRequest servletPR;
servletPR=(XSQLServletPageRequest)getPageRequest();
HttpServletRequest req=servletPR.getHttpServletRequest();
String header=getAttributeAllowingParam(“header”,actionElement);
String paramName=getAttributeAllowingParam(“param”,actionElement);
String headerValue=req.getHeader(header);
headerValue=(headerValue==null?””:headerValue);
if (paramName!=null && paramName.length()>0) {
servletPR.setPageParam(paramName,headerValue);
}
}
}
}
Custom Action Handlers 511
You can invoke this code with the following XSQL page, which uses the action
handler.
</page>
The parameter host-param will hold the value of the host header that came with
the HTTP request. Since the action handler uses the getAttributeAllowingParam
method, you can dynamically set the header that you desire. Here is a simple example
where you can specify that you want any header displayed just by setting the header
name in the URL:
</page>
The header values aren’t the most interesting values that come as part of the request.
For instance, you can also access the name of a remote user if HTTP authentication is
used. The following action handler makes all the various request variables available as
page parameters:
import oracle.xml.xsql.XSQLActionHandlerImpl;
import oracle.xml.xsql.XSQLServletPageRequest;
import oracle.xml.xsql.XSQLPageRequest;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import javax.servlet.http.HttpServletRequest;
Element actionElement=getActionElement();
if (getPageRequest().getRequestType().equals(“Servlet”)) {
XSQLServletPageRequest servletPR=
(XSQLServletPageRequest)getPageRequest();
HttpServletRequest req=servletPR.getHttpServletRequest();
String varName=getAttributeAllowingParam(“var-name”,actionElement);
String paramName=getAttributeAllowingParam(“param”,actionElement);
if (varName==null) {
return;
}
varName=varName.toUpperCase();
String varVal=””;
if (varName.equals(“AUTHTYPE”)) {
varVal=req.getAuthType();
}
else if (varName.equals(“CONTEXTPATH”)) {
varVal=req.getContextPath();
}
else if (varName.equals(“METHOD”)) {
varVal=req.getMethod();
}
else if (varName.equals(“PATHINFO”)) {
varVal=req.getPathInfo();
}
else if (varName.equals(“PATHTRANSLATED”)) {
varVal=req.getPathTranslated();
}
else if (varName.equals(“QUERYSTRING”)) {
varVal=req.getQueryString();
}
else if (varName.equals(“REMOTEUSER”)) {
varVal=req.getRemoteUser();
}
else if (varName.equals(“REQUESTURI”)) {
varVal=req.getRequestURI();
}
else if (varName.equals(“REQUESTURL”)) {
varVal=req.getRequestURL().toString();
}
else if (varName.equals(“SERVLETPATH”)) {
Custom Action Handlers 513
varVal=req.getServletPath();
}
varVal=(varVal==null?””:varVal);
if (paramName!=null && paramName.length()>0) {
servletPR.setPageParam(paramName,varVal);
}
}
}
}
In these examples, the action handlers didn’t travel to the database at all. Of course,
you can set database data as a parameter value if you like. You may find XSQLAc-
tionHandlerImpl’s firstColumnOfFirstRow(), and firstNColumnsOf-
FirstRow() methods especially useful for this purpose.
You can also set stylesheet parameters from inside your action handlers. You use the
setStylesheetParameter() method of the XSQLPageRequest class as follows:
XSQLPageRequest pageRequest=getPageRequest();
pageRequest.setStylesheetParameter(“parameterName”,”parameterValue”);
import oracle.xml.xsql.XSQLActionHandlerImpl;
import oracle.xml.xsql.XSQLPageRequest;
import org.w3c.dom.Node;
import org.w3c.dom.Element;
import javax.servlet.http.HttpServletRequest;
int count;
XSQLPageRequest pageRequest=getPageRequest();
Integer countObj=(Integer)pageRequest.getRequestObject(“count”);
if (countObj==null) {
count=0;
} else {
count=countObj.intValue();
count++;
}
pageRequest.setRequestObject(“count”,new Integer(count));
addResultElement(result,”count”,””+count);
}
}
<page xmlns:xsql=”urn:oracle-xsql”
connection=”momnpup”>
<xsql:action handler=”Counter” />
<xsql:action handler=”Counter” />
<xsql:action handler=”Counter” />
<xsql:action handler=”Counter” />
<xsql:action handler=”Counter” />
</page>
you will get the results shown in Figure 18.3. Since you can pass any type of object, the
application of this technique can get quite complex. However, passing data between
action handlers also binds them. For instance, if you have action handler A and action
handler B, and action handler B always expects data that action handler A sets, you
might not be able to use B effectively without A. This may not be a problem for any
given set of action handlers. However, such dependencies can reduce the potential for
code reuse.
Custom Action Handlers 515
Moving On
This chapter showed you how to create your own custom action handlers. This is the
key point of extension for the XSQL framework. With a solid understanding of how to
write your own action handlers, you have greatly increased your capabilities. If a built-
in action can’t handle your task, you can just write your own action. The next chapter
discusses serializers and shows you how to use them to extend XSQL.
CHAPTER
19
Serializers
Now that you’ve mastered action handlers, it’s time for you to learn the last piece of
the puzzle: serializers. A serializer controls precisely how a datagram is written to the
output stream. The serializer can be called either after an XSLT transformation or prior
to an XSLT transformation. This chapter shows you the ins and outs of serializers.
Before going further, it’s important to put serializers into perspective. They are not
as useful or as widely used as action handlers. They are capable of solving the same
problems of stylesheets, but for most tasks stylesheets are better suited for the job. As
you’ll see, serializers are best utilized when you need to send binary data.
Now that serializers have been busted down to size, it’s time to take a closer look at
them. The first section serves as a general overview of serializers and also shows you
how to write your own. The second section looks at a specific serializer: the FOP seri-
alizer. This serializer allows you create PDF documents. Used in combination with
XSQL, you can use it to generate very nice reports of real-time data.
Serializer Overview
The best way to understand serializers is to create one. You’ll do that in this section. The
first step is learning the place of serializers in the overall XSQL architecture. With a solid
understanding of how serializers fit in, you’ll create a couple of your own. This section
517
518 Chapter 19
ends with a comparison of serializers and XSLT stylesheets. They occupy the same place
in the overall framework, so it is important to know the best time to use each.
<ROWSET>
<ROW>
....
</ROW>
</ROW>
....
</ROW>
</ROWSET>
<ROWSET>
<ROW>
....
</ROW>
</ROW> Custom Serializer
....
</ROW>
</ROWSET>
<ROWSET>
<ROW>
....
</ROW>
</ROW> Custom Serializer
....
</ROW>
</ROWSET>
Using Serializers
There are two ways you can invoke a serializer—after an XSLT transformation or in place
of an XSLT transformation. Both methods are diagrammed in Figure 19.1. If you don’t do
an XSLT transformation first, your serializer will have to understand the raw XML data-
gram. If you are using the xsql:query action, for instance, the serializer will have to
work with the canonical format. In most cases, you’ll want to do some kind of transfor-
mation first. The FOP serializer works this way. Serializers that expect XML to be valid in
accordance with a particular schema usually require an XSLT transformation.
520 Chapter 19
In both cases, you invoke a serializer by including a handler attribute in the xml-
stylesheet processing instruction. The following is an example where the XSLT
processor is invoked prior to the serializer:
<?xml-stylesheet type=”text/xsl”
href=”someStylesheet.xsl”
serializer=”java:some.pkg.SomeSerializer”?>
If you want the serializer to process the raw XSQL datagram, you can simply omit
the href attribute:
<?xml-stylesheet type=”text/xsl”
serializer=”java:some.pkg.SomeSerializer”?>
In most cases, you will want to run a stylesheet before handing the XML over to a
serializer. This is especially true for serializers that utilize other technologies, such as
SVG or FOP. FOP, for instance, expects its data to be in agreement with a certain
schema. If you are writing your own custom serializer, you may find it easier to just
process the raw datagram. However, this does make your work less reusable. What if
you want to use your code outside of XSQL? You might have to transform the data to
match the canonical datagram schema. Instead, you’ll probably want to create your
own schema for your custom serializer.
FOP Architecture
FOP uses the second part of the XSL family: XSL-FO. You’re very familiar with XSLT,
the first part. The aim of XSL-FO is quite different from XSLT. While XSLT is an XML
application that gives you the ability to transform an XML document to some kind
of output, XSL-FO is aimed at giving you great control over how the document
Serializers 521
looks. FOP is open source software that allows you to process and render XSL-FO
documents.
You can think of XSL-FO as an attempt to overcome the presentation shortcomings
of HTML and CSS. For instance, how many times have you struggled to get your ele-
ments positioned on the page just right? Once you get it right on one browser, you have
to check other browsers. How many times have you printed a document and taken it
with you to read, only to find that the printer had chopped off the last four words on
the right-hand side? XSL-FO allows you to define exactly how items fit on a page and
what the size and orientation of the page is. Assuming enough tool support, you could
create a good-looking printed book by using XSL-FO. This isn’t really true with HTML
and CSS.
However, there isn’t a lot of client software out there that understands XSL-FO
directly. Maybe at some point, all the Web browsers will be able to accept and render
XSL-FO documents just as they can handle HTML documents today. For now, you
use XSL-FO to create one of the following established formats that meets the same
goals as XSL-FO:
You may be wondering, What does XSL-FO bring to the table? Why not just write
one of these formats directly? First, you’d lose the benefits of a strong open standard.
You’d also have to learn the intricacies of the underlying standard. Perhaps one the
best benefits of XSL-FO is that you can easily use it in conjunction with XSLT
stylesheets. You can transform an XSQL datagram in to an XSL-FO document. The
XSQL FOP serializer hands it to FOP, which outputs the appropriate format. The archi-
tecture appears in Figure 19.2.
From the standpoint of the developer, you can consider that XSL-FO replaces
HTML. Instead of writing an XSLT stylesheet that transforms the datagram in to
HTML, you transform the datagram in to XSL-FO. Then, the serializer creates the out-
put format. This is usually PDF. The XSQL FOP serializer that you’ll look at in the next
section writes to PDF. However, if you need to write to one of the other standards you
can easily write your own serializer.
The details of XSL-FO aren’t covered in this book. Appendix A points to some
resources online that can help you learn and use XSL-FO.
522 Chapter 19
XSQL
Datagram
<ROWSET>
<ROW>
....
</ROW>
</ROW>
....
</ROW>
</ROWSET>
PDF
Document
XSLT
Stylesheet
Client
wrapper.classpath=c:\xsql\Fop-0.19.0-CVS\fop.jar
wrapper.classpath=c:\xsql\Fop-0.19.0-CVS\lib\avalon-framework-cvs-
20020315.jar
Serializers 523
wrapper.classpath=c:\xsql\Fop-0.19.0-CVS\lib\batik.jar
wrapper.classpath=c:\xsql\Fop-0.19.0-CVS\lib\xalan-2.3.1.jar
wrapper.classpath=c:\xsql\Fop-0.19.0-CVS\lib\xercesImpl-2.0.1.jar
wrapper.classpath=c:\xsql\Fop-0.19.0-CVS\lib\xml-apis.jar
This takes care of FOP. You also need to make sure that the XSQL FOP serializer itself
is installed. Here is the line that you need:
wrapper.classpath=C:\xsql\lib\xsqlserializers.jar
There should be a nickname for the XSQL FOP serializer in the XSQLConfig.xml
file. You’ll need to have the nickname in place for the samples to work correctly. You’ll
find it in the serializerdefs element. This is what it looks like:
<serializer>
<name>FOP</name>
<class>oracle.xml.xsql.serializers.XSQLFOPSerializer</class>
</serializer>
You should be ready to go. There should be an FOP directory underneath your
demos. If you installed XSQL on a local Web server, you should be able to access the
demo with http://localhost/xsql/fop/emptable.xsql. This produces the
output shown in Figure 19.3.
Here’s a closer look at what is going on. The XSQL looks quite like what you’ve seen
before, except that a serializer is specified:
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”emptablefo.xsl”
serializer=”FOP”?>
<xsql:query connection=”demo” xmlns:xsql=”urn:oracle-xsql”>
SELECT ENAME, SAL FROM EMP
ORDER BY SAL asc
</xsql:query>
<?xml version=”1.0”?>
<fo:root xmlns:fo=”http://www.w3.org/1999/XSL/Format” xsl:version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<fo:flow flow-name=”xsl-region-body”>
</fo:table-cell>
<fo:table-cell>
<fo:block><xsl:value-of select=”SAL”/></fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:for-each>
</fo:table-body>
</fo:table>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
The best way to get an idea as to what the serializer is doing is to look at the XML
that the serializer processes. You can do this by simply commenting out the serializer
attribute in the XSQL page. This yields a document like the following. The document
here is an abbreviated version of the document used to produce the PDF in Figure 19.3;
it includes only three of the rows.
<?xml version=”1.0” ?>
<fo:root xmlns:fo=”http://www.w3.org/1999/XSL/Format”>
<fo:layout-master-set>
<fo:simple-page-master master-name=”first”
page-height=”29.7cm”
page-width=”21cm”
margin-top=”1cm”
margin-bottom=”2cm”
margin-left=”2.5cm”
margin-right=”2.5cm”>
<fo:region-body margin-top=”3cm” />
</fo:simple-page-master>
</fo:layout-master-set>
<fo:page-sequence master-name=”first”>
<fo:flow flow-name=”xsl-region-body”>
<fo:block font-size=”24pt”
font-family=”Garamond”
line-height=”24pt”
space-after.optimum=”3pt”
font-weight=”bold”
start-indent=”15pt”>
Total of All Salaries is $14650
</fo:block>
<fo:block border-width=”2pt”>
<fo:table>
<fo:table-column column-width=”4cm” />
<fo:table-column column-width=”4cm” />
<fo:table-body font-size=”10pt”
font-family=”sans-serif”>
<fo:table-row line-height=”12pt”>
<fo:table-cell>
<fo:block>SMITH</fo:block>
</fo:table-cell>
<fo:table-cell>
526 Chapter 19
<fo:block>800</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row line-height=”12pt”>
<fo:table-cell>
<fo:block>JAMES</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block>950</fo:block>
</fo:table-cell>
</fo:table-row>
<fo:table-row line-height=”12pt”>
<fo:table-cell>
<fo:block>ALLEN</fo:block>
</fo:table-cell>
</fo:table-row>
</fo:table-body>
</fo:table>
</fo:block>
</fo:flow>
</fo:page-sequence>
</fo:root>
It looks quite a bit similar to HTML, but note that fonts and margins are defined precisely.
This is the beauty of XSL-FO—that you are able to describe exactly what you want. A full
treatment of XSL-FO is beyond the scope of this book. For more information, you should
visit http://xml.apache.org/fop/index.html. This Web site not only describes
the Apache FOP project but also provides links to resources for XSL-FO and describes the
parts of the XSL-FO specification that aren’t yet covered by Apache FOP.
Now it’s time for some examples, starting with a simple text serializer.
Text Serializers
As discussed previously, there isn’t a lot that you can accomplish with a text serializer
that you can’t handle with an XSLT stylesheet. But from a learning perspective, the text
serializer can be easier to understand. Here is a simple text serializer that outputs the
skeleton of an XML document as HTML. Only the names of the elements are written.
import oracle.xml.xsql.XSQLPageRequest;
import oracle.xml.xsql.XSQLDocumentSerializer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import java.io.PrintWriter;
String mimeType=”text/html”;
String encoding=env.getPageEncoding();
if (encoding!=null && encoding.length()>0) {
mimeType=mimeType+”;charset=”+encoding;
}
env.setContentType(mimeType);
PrintWriter out=env.getWriter();
The content type is set in the preceding lines. If a page encoding is specified in the
XSQL page, it will be attached to the mime-type. You get the PrintWriter from the
XSQLPageRequest object. Optionally, you could call getOutputStream() and write
to a stream instead. However, you should try to write only to one or the other. The
remainder of the serialize() method sets up the beginning and ending HTML and
calls the displayElement() method.
Element docElem=doc.getDocumentElement();
out.println(“<html>”);
out.println(“<head><title>”+docElem.getTagName()+”</title></head>”);
out.println(“<body><H1>Document: “+docElem.getTagName()+”</H1>”);
out.println(“<table>”);
displayElement(doc.getDocumentElement(),out,0);
out.println(“</table>”);
}
528 Chapter 19
out.println(“<tr><td>”);
out.println(getSpaces(level)+”<b>”+elem.getTagName()+”</b>”);
out.println(“</td><tr>”);
NodeList list=elem.getChildNodes();
Node n=list.item(i);
if (n.getNodeType()==Node.ELEMENT_NODE) {
displayElement((Element)n,out,level+1);
}
}
}
The final method in the serializer is used to set the spacing. Three spaces are set for
each level in depth.
String s=””;
Now you need to invoke your serializer. The following XSQL page will apply only
the serializer to the XSQL datagram. Since no stylesheet is specified, no XSLT transfor-
mation will be performed prior to invoking the serializer.
<?xml version=”1.0”?>
<?xml-stylesheet serializer=”java:SimpleTextSerializer”?>
<page xmlns:xsql=”urn:oracle-xsql” connection=”demo”>
<xsql:query>
Serializers 529
This produces the output shown in Figure 19.4. For this example, you can see that the
elements for the raw datagram are listed.
You can also apply a serializer to the results of an XSLT transformation. The follow-
ing XSQL page transforms the datagram with a stylesheet. The serializer is then called
to process the results of that transformation.
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl” href=”emp-serializer.xsl”
serializer=”java:SimpleTextSerializer”?>
<page xmlns:xsql=”urn:oracle-xsql” connection=”demo”>
<xsql:query>
SELECT ename, job, sal FROM emp
WHERE deptno=20
ORDER BY sal
</xsql:query>
</page>
This produces the output shown in Figure 19.5. As you can see, the serializer processed
the XHTML markup specified by the stylesheet.
These examples should give you a sense of how to use serializers, both with and
without stylesheets. As with action handlers, the most important skill is learning how
to navigate a document object. Once you learn how to navigate the DOM, you can do
almost anything you want with a serializer.
Binary Serializers
Writing a binary serializer is almost exactly the same as writing a text serializer. You
declare the content type and then write the output. Instead of using the writer for out-
put, you use an OutputStream.
The following code is a simple binary serializer that reads an image file from the file
system and writes it out. In this example, the XML input is ignored.
import oracle.xml.xsql.XSQLPageRequest;
import oracle.xml.xsql.XSQLDocumentSerializer;
import java.io.OutputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
Serializers 531
import java.io.PrintWriter;
import org.w3c.dom.Document;
String mimeType=”image/gif”;
try {
String fileName=”c:\\data\\pics\\image.gif”;
FileInputStream fileIn=new FileInputStream(fileName);
BufferedInputStream in=new BufferedInputStream(fileIn);
OutputStream rawOut=env.getOutputStream();
BufferedOutputStream out=new BufferedOutputStream(rawOut);
env.setContentType(mimeType);
out.flush();
out.close();
in.close();
} catch (Exception e) {
env.setContentType(“text/plain”);
PrintWriter out=env.getWriter();
out.println(“An error has occurred”);
out.println(e.getMessage());
}
}
}
The following is an XSQL page that invokes this serializer. It follows the same rules
as those of a text serializer. The only thing required is that you must specify the serial-
izer in the xml-stylesheet processing instruction. Since the XML input is ignored in
the simple example, it doesn’t matter what XSQL is used or whether a stylesheet pre-
cedes transforms the datagram.
<?xml version=”1.0”?>
<?xml-stylesheet serializer=”java:SimpleBinarySerializer”?>
<dummy/>
Of course, a binary serializer can use the datagram or a transformation of the data-
gram. In this example, the file that this serializer writes is hard-coded. You could write
the serializer so that the filename is derived from a database query of some sort. You
dictate that the serializer expects the input XML to appear as follows:
<?xml version=”1.0”?>
<binary-data>
<content-type>image/gif</content-type>
<file-name> c:\data\pics\image.gif</file-name>
</binary-data>
Instead of hardcoding the content type and the filename in the serializer, you can
derive these from the XML input. This is how the new serialize() method will look:
XMLElement docElem=(XMLElement)doc.getDocumentElement();
try {
String mimeType=docElem.valueOf(“content-type”);
String fileName=docElem.valueOf(“file-name”);
FileInputStream fileIn=new FileInputStream(fileName);
BufferedInputStream in=new BufferedInputStream(fileIn);
env.setContentType(mimeType);
OutputStream rawOut=env.getOutputStream();
BufferedOutputStream out=new BufferedOutputStream(rawOut);
out.flush();
out.close();
Serializers 533
in.close();
} catch (Exception e) {
env.setContentType(“text/html”);
PrintWriter out=env.getWriter();
out.println(“<html><head><title>Error</title></head>”);
out.println(“<body>”);
out.println(“An error has occurred”);
out.println(e.getMessage());
out.println(“</body></html>”);
If you want to derive the content type and the filename from the database, you just
query the database with an action, as follows:
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl”
href=”binary-style.xsl”
serializer=”java:SimpleBinarySerializer”?>
<xsql:query xmlns:xsql=”urn:oracle-xsql” connection=”demo”>
SELECT file-name, content-type
FROM binary_table
WHERE id={@some-param}
</xsql:query>
The last piece of the puzzle is the stylesheet that formats the query results so that the
serializer can use them. The following stylesheet should do the trick:
<?xml version=”1.0”?>
<xsl:stylesheet
version=”1.0”
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”>
<xsl:template match=”/”>
<binary-data>
<content-type>
<xsl:value-of select=”/ROWSET/ROW/content-type”/>
</content-type>
<file-name>
<xsl:value-of select=”/ROWSET/ROW/file-name”/>
</file-name>
</binary-data>
</xsl:template>
</xsl:stylesheet>
534 Chapter 19
In this example, you define a schema that your serializer accepts and then aim your
XSQL and XSLT stylesheet to fit that schema. In this case, you used a built-in action to
provide the data, but you could certainly write your own action handler to provide the
data.
This example is still fairly simple, though. You aren’t creating binary data based on
the XML input. Instead, you are just choosing the source of binary data. In a lot of
cases, you won’t do much more than choose a source and stream it to the client with
the correct content type. It’s easy to create and mold text on the fly, but doing so with
binary data is more difficult. If your serializer is truly going to create the binary data
from scratch, you’ll have to have a good understanding of the underlying binary stan-
dard of the data. In most cases, you’ll probably be able to find some kind of encoder
that knows how write the binary data, and then you just have to pass it to input. FOP
generates the PDFs in this manner.
Examples of these cases are provided in the next sections. Because of the nature of
binary data, your serializers tend to be either very simple or very complex.
If you are creating binary data from scratch, your serializer will be complex. You
have to have detail.
Serializing BLOBs
In the previous example, you streamed an image that was stored on the file system.
You can also stream binary data that exists in the database as a BLOB. In the text that
follows, you’ll see how to use a serializer to deliver BLOBs to your clients. In fact, if
there is binary data in the database that you wish to push to the client, you will have to
use a serializer both to write it and to fetch it from the database.
From our earlier discussions, it’s obvious that you have to use a serializer to deliver
binary data from XSQL. But in contrast to text data, you can’t really provide binary
data as input to a serializer. The serializer takes XML as input, and XML is a text for-
mat. You could encode the binary data in an action handler, but doing this is more trou-
ble than its worth. It also avoids an important problem: Binary files tend to be large,
and for large files, you would want to stream them rather than store them in memory.
If you wish to serialize a BLOB, your first challenge will be to determine what BLOB
you want. You should be able to figure this out by examining the XML input that is pro-
vided. The following sample is a general-purpose serializer that takes an SQL state-
ment as input.
import oracle.xml.xsql.XSQLPageRequest;
import oracle.xml.xsql.XSQLDocumentSerializer;
import oracle.xml.parser.v2.XMLElement;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.InputStream;
import org.w3c.dom.Document;
import oracle.jdbc.driver.OracleResultSet;
import oracle.sql.BLOB;
Serializers 535
import java.sql.ResultSet;
import java.sql.Statement;
import java.sql.DriverManager;
import java.sql.Connection;
try {
XMLElement docRoot=(XMLElement)doc.getDocumentElement();
String mimeType=docRoot.valueOf(“./mime-type”);
String cmd =docRoot.valueOf(“./sql-statement”);
String connectStr=docRoot.valueOf(“./connect-string”);
String username=docRoot.valueOf(“./username”);
String password=docRoot.valueOf(“./password”);
Connection conn=DriverManager.getConnection(connectStr,
username,
password);
OutputStream out=env.getOutputStream();
env.setContentType(mimeType);
blobStream.close();
conn.close();
} catch (Exception e) {
536 Chapter 19
env.setContentType(“text/plain”);
PrintWriter out=env.getWriter();
out.println(“An error has occurred”);
out.println(e.getMessage());
}
}
}
This code uses JDBC to access the database and Oracle JDBC connections to handle the
BLOB data. In this example, the connection is handled in the simplest manner. How-
ever, it does mean that you’ll be opening and closing a connection on each request.
Some kind of connection pooling mechanism would be better suited for this serializer.
To use the serializer, it needs an XML document that looks like this:
<?xml version=”1.0”?>
<blob-serializer>
<sql-statement>
SELECT image FROM image_table
WHERE id=5
</sql-statement>
<mime-type>image/jpeg</mime-type>
<connect-string>jdbc:oracle:thin:@localhost:1521:ORCL</connect-string>
<username>momnpup</username>
<password>momnpup</username>
</blob-serializer>
This means that one of the products of your XSLT transformation is an SQL statement.
To accomplish this, you can use the same techniques that you learned in Chapter 15. But
first, you need an XSQL page. Here is a simple one that assumes that the image_id in
the product table points to the image_table:
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl”
href=”simple-blob-serializer.xsl”
serializer=”java:SimpleBlobSerializer”?>
<xsql:query connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”
rowset-element=”blob-serializer”
row-element=”row”>
This is just one possible example. The idea is that you get some data that can be used
to locate the appropriate BLOB. The stylesheet then creates the XML that the serializer
expects. Here is a stylesheet that creates a SQL query that grabs a JPEG image out of the
image_table:
<xsl:template match=”/”>
<blob-serializer>
<sql-statement>
SELECT image FROM image_table
WHERE id=<xsl:value-of select=”/BLOB-SERIALIZER/ROW/IMAGE_ID”/>
</sql-statement>
<mime-type>image/jpeg</mime-type>
<connect-string>jdbc:oracle:thin:@localhost:1521:ORCL</connect-string>
<username>momnpup</username>
<password>momnpup</password>
</blob-serializer>
</xsl:template>
</xsl:stylesheet>
In this example, only one value is dynamic—the image_id. Of course, you could
dynamically generate the other values. You could, for instance, store both GIF and
JPEG images in the same table and then have a column that specifies the mime-type.
N OT E As you read this, you’ll probably wonder if the task at hand could be
more easily accomplished with the use of SVG. Absolutely. SVG gives you far
more capability, with greater ease, than the serializer that you are creating
here. You can even use the Batik package from Apache to directly render SVG
images as JPEGs. This example is meant to demonstrate the basic process of
integrating AWT with a XSQL serializer.
538 Chapter 19
This serializer will create a bar chart of a series of data. Each bar will be labeled on
the left, and the actual value will be written on the right after the bar. The serializer
will also expect a scaling factor so that the bars can be rendered correctly for the image
size. Figure 19.6 shows what the finished product can produce based on data in the
database.
With these basic requirements, your first step is to define what the input will look
like. Here is a sample XML file that meets the requirements:
<barcolor red=”0”
green=”0”
blue=”255”/>
</bar>
<bar leftmargin=”10”
topmargin=”10”
bottommargin=”10”
height=”10”
value=”5000”>
<title width=”100”>
<text>KING</text>
<textcolor red=”255”
green=”0”
blue=”0”/>
</title>
<barcolor red=”0”
green=”0”
blue=”255”/>
</bar>
<bar leftmargin=”10”
topmargin=”10”
bottommargin=”10”
height=”10”
value=”1300”>
<title width=”100”>
<text>MILLER</text>
<textcolor red=”255”
green=”0”
blue=”0”/>
</title>
<barcolor red=”0”
green=”0”
blue=”255”/>
</bar>
</graph>
With an idea of what the input should look like, you can write the serializer. The
basic design is much the same as the previous examples. You first interpret the
inputted XML, and the last thing you do is write the data out to the client. What is
added here is the actual creation of the binary object. You use the AWT libraries to cre-
ate an image. You then write the text and draw the bars onto the image. The architec-
ture of what you’ll accomplish in the next few pages is diagrammed in Figure 19.7.
540 Chapter 19
XSQL
Datagram
<ROWSET>
<ROW>
....
</ROW>
<ROW>
....
</ROW>
</ROWSET>
JPEG
XSLT
Stylesheet
Client
At this point, you know what your serializer should accept as input, so you can go
ahead and start coding it. Here is the requisite class information and the serialize
method:
import oracle.xml.xsql.XSQLPageRequest;
import oracle.xml.xsql.XSQLDocumentSerializer;
import oracle.xml.parser.v2.XMLElement;
import java.io.OutputStream;
import java.io.PrintWriter;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
import java.awt.image.BufferedImage;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.Line2D;
import java.awt.Color;
Serializers 541
import org.w3c.dom.Document;
import org.w3c.dom.NodeList;
import org.w3c.dom.Node;
float scale=1;
/**
* Takes an XML document that behaves the previously defined
* schema and outputs a JPEG image created using AWT
*/
String mimeType=”image/jpeg”;
try {
XMLElement rootElem=(XMLElement)doc.getDocumentElement();
} catch (Exception e) {
542 Chapter 19
env.setContentType(“text/html”);
PrintWriter out=env.getWriter();
out.println(“An error has occurred”);
out.println(e.getMessage());
e.printStackTrace(out);
}
}
The preceding serialize() method sets up the initial image based on top-level
parameters, calls the drawBars() method to do the actual drawing, and the uses the
JPEGCodec class to write the JPEG data to the output stream. If any exception is
thrown, an error message will be printed as an HTML file. The next method to exam-
ine is the drawBars() method:
int startX=20;
int startY=20;
XMLElement bar=(XMLElement)elems.item(i);
startY=drawBar(g,bar,startX,startY);
}
}
The main function of this method is to manage the position where the next bar
should be drawn. The starting point is hard-coded in this example, but it could certainly
be configurable. The real work of drawing is handled in the drawBar() method:
String topMarginStr=bar.valueOf(“./@topmargin”);
int topMargin=Integer.parseInt(topMarginStr);
String bottomMarginStr=bar.valueOf(“./@bottommargin”);
int bottomMargin=Integer.parseInt(bottomMarginStr);
String heightStr=bar.valueOf(“./@height”);
int height=Integer.parseInt(heightStr);
String valueStr=bar.valueOf(“./@value”);
int value=Integer.parseInt(valueStr);
String title=bar.valueOf(“./title”);
String titleWidthStr=bar.valueOf(“./title/@width”);
int titleWidth=Integer.parseInt(titleWidthStr);
Node colorNode=bar.selectSingleNode(“./barcolor”);
Color barColor=getColor((XMLElement)colorNode);
Serializers 543
colorNode=bar.selectSingleNode(“./title/textcolor”);
Color textColor=getColor((XMLElement)colorNode);
title=title.trim();
// Do the drawing
The code of this method is divided between two purposes: reading the data from the
XML element for a particular bar and drawing the bar. A custom serializer will always
need some strategy for reading its input from the XML document. What you do
with that data varies widely. In this case, you are doing some simple drawing using
AWT, but you are really unlimited in what you can do. The remainder of the class is
displayed as follows. It consists of several helper functions.
String widthStr=elem.valueOf(“./image/@width”);
return Integer.parseInt(widthStr);
String heightStr=elem.valueOf(“./image/@height”);
return Integer.parseInt(heightStr);
}
544 Chapter 19
String scaleStr=elem.valueOf(“./image/@scale”);
if (scaleStr!=null) {
return Float.parseFloat(scaleStr);
} else {
return 1;
}
String redStr=elem.valueOf(“@red”);
String greenStr=elem.valueOf(“@green”);
String blueStr=elem.valueOf(“@blue”);
int red=Integer.parseInt(redStr);
int green=Integer.parseInt(greenStr);
int blue=Integer.parseInt(blueStr);
}
}
Since this last snippet of code concludes the class, the last brace is the closing brace
for the class. Your next steps are writing the XSQL and XSLT pages. It doesn’t really
matter what your XSLT pages look like, as long as they produce an XML document that
obeys the schema for this serializer. The XSQL and XSLT pages presented here produce
the chart displayed earlier in this chapter. Here’s the XSQL page:
<?xml version=”1.0”?>
<?xml-stylesheet type=”text/xsl”
href=”jpeg-graph-serializer.xsl”
serializer=”java:JpegGraphSerializer”?>
<page connection=”demo”
xmlns:xsql=”urn:oracle-xsql”>
<xsql:query>
SELECT ename, sal FROM emp
WHERE deptno={@deptno}
</xsql:query>
<xsql:set-stylesheet-param name=”max-value”>
SELECT max(sal) FROM emp
WHERE deptno={@deptno}
</xsql:set-stylesheet-param>
</page>
Serializers 545
This is pretty standard fare. The only thing that differs from what you’ve seen many
times before is the serializer attribute in the stylesheet processing instruction. The
stylesheet, displayed as follows, also requires the max-value parameter. This param-
eter isn’t required of the serializer, but it is of the stylesheet.
<?xml version=”1.0”?>
<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”
version=”1.0”>
<xsl:param name=”max-value”/>
<xsl:param name=”width”>1000</xsl:param>
<xsl:param name=”title_width”>100</xsl:param>
<xsl:param name=”scale-factor”><xsl:value-of select=”800 div $max-
value”/></xsl:param>
<xsl:param name=”height”><xsl:value-of
select=”count(/page/ROWSET/ROW)*30+50”/></xsl:param>
<xsl:template match=”/page”>
<graph>
<image>
<xsl:attribute name=”height”><xsl:value-of
select=”$height”/></xsl:attribute>
<xsl:attribute name=”width”><xsl:value-of
select=”$width”/></xsl:attribute>
<xsl:attribute name=”scale”><xsl:value-of select=”$scale-
factor”/></xsl:attribute>
<background red=”200” green=”200” blue=”200”/>
</image>
<xsl:apply-templates select=”ROWSET/ROW”/>
</graph>
</xsl:template>
<xsl:template match=”ROW”>
<bar leftmargin=”10” topmargin=”10” bottommargin=”10” height=”10”>
<xsl:attribute name=”value”><xsl:value-of
select=”SAL”/></xsl:attribute>
<title>
<xsl:attribute name=”width”><xsl:value-of
select=”$title_width”/></xsl:attribute>
<text><xsl:value-of select=”ENAME”/></text>
<textcolor red=”255” green=”0” blue=”0”/>
</title>
<barcolor red=”0” green=”0” blue=”255”/>
</bar>
</xsl:template>
</xsl:stylesheet>
546 Chapter 19
The majority of this stylesheet works to create the bar elements needed by the seri-
alizer. The first few lines, though, are occupied in specifying the size correctly based on
the number of rows of data and the correct scaling factor. By leaving this work to the
stylesheet, you can have a simpler serializer that can handle a lot of different types of
data.
Moving On
In this chapter you have learned how to use the final tool in the XSQL arsenal—
serializers. They are especially apt for the creation of binary data. By using serializers,
you can create any kind of data presentation that you wish. Often, this will mean lever-
aging existing tools such as FOP, but you are also free to write the binary formats
directly from the ground up.
You now know all you need to know to easily create robust applications by using
XSQL. You probably learned a number of new paradigms along the way, but hopefully
you can now see how XSQL can make applications easily. Have fun!
ACPHPAEPNTDE IRX
Resources
This appendix covers some resources that you should find useful as you develop XSQL
applications.
Oracle Technet
The Oracle Technology Network’s Web site resides at http://technet
.oracle.com. You’ll need to register. Once you’ve registered, you’ll have access to a
great variety of resources about Oracle, including white papers, product documenta-
tion, and discussion forums. The most relevant discussion forum for XSQL is the XML
forum.
547
548 Appendix A
PL/SQL Reference
This manual covers all you need to know about PL/SQL. You’ll find the manual at
http://otn.oracle.com/docs/products/oracle9i/doc_library/
release2/appdev.920/a96624/toc.htm.
In this book, you used just a couple of supplied PL/SQL packages. Oracle offers a ton
that you didn’t use. They are described in a document at http://otn.oracle.com/
docs/products/oracle9i/doc_library/release2/appdev.920/a96612/
toc.htm.
Oracle XML DB
The Oracle XML DB offers information about the XML storage functionality of the
Oracle database. There is a lot of information at this site about the Oracle XMLType
that you learned about in Chapter 11, which discussed using Oracle Text with XML
documents. You’ll find the information at http://otn.oracle.com/docs/prod-
ucts/oracle9i/doc_library/release2/appdev.920/a96620/toc.htm.
XSLT Resources
XSLT is extremely important in XSQL development. As a popular standard, there is a
lot of information available on the Web. The definitive source of information is the
XSLT Recommendation from W3C. You’ll find the information at http://www
.w3.org/TR/xslt.
However, this document covers a lot of information only needed by developers
implementing an XSLT processor. A better starting point for the beginning XSLT devel-
oper is http://www.xslt.com.
Another great resource is available at zvon.org. This site provides an interactive
guide to all of the XSLT elements and XPath. You’ll find the guide at http://www
.zvon.org/xxl/XSLTreference/Output/index.html.
Java Resources
The definitive resource for Java is http://www.javasoft.com. You can also find
information that is specific to Oracle and Java at the XDK links listed earlier.
ACPHPAEPNTDE IRX
Related Standards
There were a few standards that were discussed briefly in the book. This appendix
covers these in a little more depth, and provides links that you may find useful.
XSL-FO
You learned a little bit about XSL-FO in Chapter 19, “Serializers.” You can use XSL-FO
in conjunction with Apache FOP to create Portable Document Format (PDF) docu-
ments. There are also other formats that you can produce. XSL-FO is its own XML
application that is quite similar to XHTML. The definitive source of information is
from the World Wide Web Consortium (W3C) recommendation at http://www.w3
.org/TR/2001/REC-xsl-20011015/#.
However, a beginner may not find the recommendation particularly useful. Also,
Apache FOP doesn’t fully implement the XSL-FO recommendation. For specific infor-
mation about how Apache FOP implements the recommendation, you should visit the
Apache FOP home page at http://xml.apache.org/fop/.
A good tutorial on creating XSL-FO documents is available from Dave Pawson at
http://www.dpawson.co.uk/xsl/sect3/bk/index.html.
551
552 Appendix B
553
554 Index
PL/SQL (Procedural Language extensions retrieving XML and, 263, 264, 266
to SQL) Web application development and, 368,
actions and, 82–84, 503 375–376
array structures, 232–234 processing instructions
control structures, 237–241 described, 17
cursors and, 229, 232, 239, 241–243, details for, 95
250–251 serializers and, 520
data types and, 136 syntax rules and, 18
declaration sections, 228–234, 245 XPath and, 346
exceptions and, 248–249 XSLT and, 326, 346
execution sections, 228, 234–325, 248–249 processing-instruction function,
installation and, 32 346
integration of, with XSQL, 251 process method, 474
resources, 548 processor element, 42–44
retrieving XML and, 261–268 processToXML method, 474
SQL statements in, 235–236 PROGRAM_ERROR exception, 249
structure of, 228 proximity searches, 259
triggers and, 175, 248–250 pseudo
Portable Document Format. See PDF files -attributes, 95
port 80, 26 -SQL, 385–387, 388, 392–396
positional notation, 247
position function, 358 Q
POSITIVE data type, 230 question mark (?), 94
POSITIVEN data type, 230
POST method R
actions and, 91 raise keyword, 248
canonical schema and, 117 raw data type, 135, 136
described, 123–127 rawtohex function, 220, 221
parameters and, 98, 102 readonly attribute, 279
Web application development and, 418, readonly=”readonly” attribute, 279
424 REAL data type, 134, 230
xsql:delete-request action and, 92 record declarations, 231–232
power function, 195–196 reference cursors, 250–251. See also cursors
preceding axis, 352 referential integrity, 176
preceding-sibling axis, 352 RELAX NG, 22
pre element, 281 reload-connections-on-error
Presentation layer, 4, 5 element, 42
PRIMARY KEY constraints, 175 replace function, 206–207
primary keys, 175, 168 request element, 117
priority attribute, 294 request parameters
PRIOR method, 233 described, 102–104
privileges, assigning, 52 names of, 105, 110–111
Procedural Language extensions to SQL. result-cache-size element, 42
See PL/SQL result-prefix attribute, 326
procedures result sets, 6
advantages of, 375–376 reuse, of code, 51, 339–340, 403
described, 245–247 ROLLBACK statement, 236
PL/SQL and, 245–247 root-element-name attribute, 506
Index 565
servlet engine (continued) skip-rows attribute, 76, 422, 423, 425, 426
high-level architecture and, 56–57 SMALLINT data type, 134, 230
installation and, 30–32 SOAP (Simple Object Access Protocol),
servlets 125, 451
action handlers and, 484–485, 492–493 sorting data, 340–342
described, 24, 60 soundex function, 208
development of, 484–485 SOUNDEX operator, 255
file extensions and, 31, 32 spatial data types, 136
functionality of, 492–493 special characters, 19–20, 132
high-level architecture and, 53–54 SQL (Structured Query Language)
installation and, 28 action handlers and, 505–507
integration of, 30–32 adding data with, 167–174
posted XML and, 124 aggregate functions, 183–192
security issues related to, 46 altering tables with, 164–166
using XSQL within, 474–475 conversion functions, 219–224
Web application development and, 377 creating tables with, 161–164
xsql:set-cookie action and, 87–88 data types and, 132–137
xsql:set-session-param action and, date formats and, 178–182
86–87 dropping tables with, 162, 166–167
See also JSP (Java Servlet Pages); servlet expressions, 137–139
engine integration of, with PL/SQL, 235–236
session parameters lexical conventions, 132
described, 97, 104–106 loading XSQL samples with, 35–36
names of, 105, 110–111 modifying data with, 167–174
Web application development and, numeric functions, 192–197
418–419 operators and, 137, 151–156, 159–160
sessiontimezone function, 218 Oracle Text and, 12
set comparison operators, 159–160 passing parameters with, 415–417
SET CONSTRAINTS statement, 236 poisoning, 46, 47–49
set-Params method, 457 scripts, writing, 392–396, 446–447
setRequestObject method, 513 syntax, 131–139
SET ROLL statement, 236 target clause and, 140–142
SET TRANSACTION statement, 236 technological superiority of, 1
SGA (System Global Memory), 26 three-tiered development model and, 4,
SGML (Standard Generalized Markup 5, 6
Language) unstructured text and, 12
disadvantages of, 14 views and, 172–174
as the precursor to HTML, 3 Web application development and, 368,
XML and, 13–14 374, 379, 392–396, 415–417
XSLT and, 277 SQL*PLUS
SHOW ERRORS command, 226 creating demo users with, 37
SIDs (System Identifiers) creating tables with, 37–39
installation and, 28, 33 date format, 178–182
replacing, 38 running install.sql with, 35–36
security issues related to, 46 SHOW ERRORS command, 226
sign function, 196 sqrt function, 197
SIGNTYPE data type, 230 standalone attribute, 292
simple expressions, 137 Standard Generalized Markup Language.
single quote (‘), 17, 18, 19 See SGML
Index 567